/// <summary> /// generate a non-deterministic choice block to call every public visible functions except constructors /// /// </summary> /// <param name="contracts"></param> /// <param name="context"></param> /// <param name="callBackTarget">If non-null, this will be the msg.sender for calling back into the contracts</param> /// <returns></returns> public static BoogieIfCmd GenerateChoiceBlock(List <ContractDefinition> contracts, TranslatorContext context, Tuple <string, string> callBackTarget = null) { BoogieIfCmd ifCmd = null; int j = 0; foreach (var contract in contracts) { if (contract.ContractKind != EnumContractKind.CONTRACT) { continue; } HashSet <FunctionDefinition> funcDefs = context.GetVisibleFunctionsByContract(contract); List <FunctionDefinition> publicFuncDefs = new List <FunctionDefinition>(); foreach (FunctionDefinition funcDef in funcDefs) { if (funcDef.IsConstructor) { continue; } if (funcDef.IsFallback) { continue; //let us not call fallback directly in harness } if (funcDef.Visibility == EnumVisibility.PUBLIC || funcDef.Visibility == EnumVisibility.EXTERNAL) { publicFuncDefs.Add(funcDef); } } for (int i = publicFuncDefs.Count - 1; i >= 0; --i) { j++; BoogieExpr guard = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, new BoogieIdentifierExpr("choice"), new BoogieLiteralExpr(j)); BoogieStmtList thenBody = new BoogieStmtList(); FunctionDefinition funcDef = publicFuncDefs[i]; string callee = TransUtils.GetCanonicalFunctionName(funcDef, context); List <BoogieExpr> inputs = new List <BoogieExpr>() { // let us just call back into the calling contract callBackTarget != null ? new BoogieIdentifierExpr(callBackTarget.Item1) : new BoogieIdentifierExpr("this"), callBackTarget != null ? new BoogieIdentifierExpr(callBackTarget.Item2) : new BoogieIdentifierExpr("msgsender_MSG"), new BoogieIdentifierExpr("msgvalue_MSG"), }; foreach (VariableDeclaration param in funcDef.Parameters.Parameters) { string name = TransUtils.GetCanonicalLocalVariableName(param); inputs.Add(new BoogieIdentifierExpr(name)); if (param.TypeName is ArrayTypeName array) { thenBody.AddStatement(new BoogieCallCmd( "FreshRefGenerator", new List <BoogieExpr>(), new List <BoogieIdentifierExpr>() { new BoogieIdentifierExpr(name) })); } } List <BoogieIdentifierExpr> outputs = new List <BoogieIdentifierExpr>(); var retParamCount = 0; foreach (VariableDeclaration param in funcDef.ReturnParameters.Parameters) { string name = $"__ret_{retParamCount++}_" + funcDef.Name; if (!string.IsNullOrEmpty(param.Name)) { name = TransUtils.GetCanonicalLocalVariableName(param); } outputs.Add(new BoogieIdentifierExpr(name)); } if (context.TranslateFlags.InstrumentGas) { havocGas(thenBody); } BoogieCallCmd callCmd = new BoogieCallCmd(callee, inputs, outputs); thenBody.AddStatement(callCmd); BoogieStmtList elseBody = ifCmd == null ? null : BoogieStmtList.MakeSingletonStmtList(ifCmd); ifCmd = new BoogieIfCmd(guard, thenBody, elseBody); } } return(ifCmd); }
public RevertLogicGenerator(TranslatorContext context) { this.context = context; this.constructorNames = context.ContractDefinitions.Select(c => TransUtils.GetCanonicalConstructorName(c)).ToHashSet(); proceduresInProgram = context.Program.Declarations.OfType <BoogieProcedure>().ToDictionary(procedure => procedure.Name); }
public static List <BoogieVariable> CollectLocalVars(List <ContractDefinition> contracts, TranslatorContext context) { Debug.Assert(contracts.Count > 0, "Internal exception: expecting at least one contract here"); List <BoogieVariable> localVars = new List <BoogieVariable>() { new BoogieLocalVariable(new BoogieTypedIdent("this", BoogieType.Ref)), new BoogieLocalVariable(new BoogieTypedIdent("msgsender_MSG", BoogieType.Ref)), new BoogieLocalVariable(new BoogieTypedIdent("msgvalue_MSG", BoogieType.Int)), new BoogieLocalVariable(new BoogieTypedIdent("choice", BoogieType.Int)), }; // use to remove duplicated variables by name HashSet <string> uniqueVarNames = new HashSet <string>() { "this", "msgsender_MSG", "msgvalue_MSG", "choice" }; foreach (var contract in contracts) { if (contract.ContractKind != EnumContractKind.CONTRACT) { continue; } // Consider all visible functions HashSet <FunctionDefinition> funcDefs = context.GetVisibleFunctionsByContract(contract); foreach (FunctionDefinition funcDef in funcDefs) { if (funcDef.Visibility == EnumVisibility.PUBLIC || funcDef.Visibility == EnumVisibility.EXTERNAL) { foreach (VariableDeclaration param in funcDef.Parameters.Parameters) { string name = TransUtils.GetCanonicalLocalVariableName(param); if (!uniqueVarNames.Contains(name)) { BoogieType type = TransUtils.GetBoogieTypeFromSolidityTypeName(param.TypeName); BoogieVariable localVar = new BoogieLocalVariable(new BoogieTypedIdent(name, type)); localVars.Add(localVar); uniqueVarNames.Add(name); } } var retParamCount = 0; foreach (VariableDeclaration param in funcDef.ReturnParameters.Parameters) { //string name = "__ret" + funcDef.Name; string name = $"__ret_{retParamCount++}_" + funcDef.Name; if (!string.IsNullOrEmpty(param.Name)) { name = TransUtils.GetCanonicalLocalVariableName(param); } if (!uniqueVarNames.Contains(name)) { BoogieType type = TransUtils.GetBoogieTypeFromSolidityTypeName(param.TypeName); BoogieVariable localVar = new BoogieLocalVariable(new BoogieTypedIdent(name, type)); localVars.Add(localVar); uniqueVarNames.Add(name); } } } } } return(localVars); }
private List <BoogieCmd> GenerateConstructorCall(ContractDefinition contract) { List <BoogieCmd> localStmtList = new List <BoogieCmd>(); string callee = TransUtils.GetCanonicalConstructorName(contract); List <BoogieExpr> inputs = new List <BoogieExpr>() { new BoogieIdentifierExpr("this"), new BoogieIdentifierExpr("msgsender_MSG"), new BoogieIdentifierExpr("msgvalue_MSG"), }; 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)); 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 { 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); inputs.Add(new BoogieIdentifierExpr(name)); if (param.TypeName is ArrayTypeName array) { localStmtList.Add(new BoogieCallCmd( "FreshRefGenerator", new List <BoogieExpr>(), new List <BoogieIdentifierExpr>() { new BoogieIdentifierExpr(name) })); } } } 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) { var gasVar = new BoogieIdentifierExpr("gas"); localStmtList.Add(new BoogieAssignCmd(gasVar, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.SUB, gasVar, new BoogieLiteralExpr(TranslatorContext.CREATE_GAS_COST)))); } localStmtList.Add(new BoogieCallCmd(callee, inputs, null)); return(localStmtList); }
private BoogieIfCmd GenerateChoices(ContractDefinition contract) { BoogieExpr thisVal = new BoogieIdentifierExpr("this"); Tuple <BoogieIfCmd, int> curChoices = TransUtils.GeneratePartialChoiceBlock(new List <ContractDefinition>() { contract }, context, thisVal, 0, context.TranslateFlags.ModelReverts); if (context.TranslateFlags.TxnsOnFields) { HashSet <VariableDeclaration> 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 <ContractDefinition>() { fieldDef }, context, fieldInstance, curChoices.Item2, context.TranslateFlags.ModelReverts, curChoices.Item1); } } } return(curChoices.Item1); }
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)); }
private BoogieStmtList GenerateBody(List <BoogieVariable> inParams) { // // foreach contract C that is not Lib/VeriSol // if (DT[this] == C) // if C has a fallback f // call ret := fallBack_C(this=to, sender=from, msg.value) // else // assume msg.value == 0; // else // call fallBack_unknownType(from, to, msg.value) BoogieIfCmd ifCmd = null; Debug.Assert(context.ContractDefinitions.Count >= 1, "There should be at least one contract"); List <BoogieExpr> arguments = new List <BoogieExpr>() { new BoogieIdentifierExpr(inParams[1].Name), new BoogieIdentifierExpr(inParams[0].Name), new BoogieIdentifierExpr(inParams[2].Name) }; List <BoogieIdentifierExpr> outParams = new List <BoogieIdentifierExpr>(); BoogieStmtList noMatchCase = BoogieStmtList.MakeSingletonStmtList( new BoogieCallCmd("Fallback_UnknownType", arguments, outParams)); foreach (var contract in context.ContractDefinitions) { if (contract.ContractKind == EnumContractKind.LIBRARY) { continue; } FunctionDefinition function = context.ContractToFallbackMap.ContainsKey(contract) ? context.ContractToFallbackMap[contract] : null; BoogieExpr lhs = new BoogieMapSelect(new BoogieIdentifierExpr("DType"), new BoogieIdentifierExpr(inParams[1].Name)); BoogieExpr rhs = new BoogieIdentifierExpr(contract.Name); BoogieExpr guard = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, lhs, rhs); BoogieStmtList thenBody = null; if (function != null) { string callee = TransUtils.GetCanonicalFunctionName(function, context); thenBody = BoogieStmtList.MakeSingletonStmtList(new BoogieCallCmd(callee, arguments, outParams)); } else { // No fallback means not payable (amount == 0) thenBody = BoogieStmtList.MakeSingletonStmtList( new BoogieAssumeCmd( new BoogieBinaryOperation( BoogieBinaryOperation.Opcode.EQ, new BoogieIdentifierExpr(inParams[2].Name), new BoogieLiteralExpr(BigInteger.Zero))) ); } BoogieStmtList elseBody = ifCmd == null ? noMatchCase : BoogieStmtList.MakeSingletonStmtList(ifCmd); ifCmd = new BoogieIfCmd(guard, thenBody, elseBody); } return(BoogieStmtList.MakeSingletonStmtList(ifCmd)); }
// generate a non-deterministic choice block to call every public visible functions except constructors private BoogieIfCmd GenerateChoiceBlock(ContractDefinition contract) { HashSet <FunctionDefinition> funcDefs = context.GetVisibleFunctionsByContract(contract); List <FunctionDefinition> publicFuncDefs = new List <FunctionDefinition>(); foreach (FunctionDefinition funcDef in funcDefs) { if (funcDef.IsConstructorForContract(contract.Name)) { continue; } if (funcDef.Visibility == EnumVisibility.PUBLIC || funcDef.Visibility == EnumVisibility.EXTERNAL) { // HACK: lets ignore "fallback_" named functions for DAO demo if (funcDef.Name.Equals("fallback")) { continue; } publicFuncDefs.Add(funcDef); } } BoogieIfCmd ifCmd = null; for (int i = publicFuncDefs.Count - 1; i >= 0; --i) { BoogieExpr guard = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, new BoogieIdentifierExpr("choice"), new BoogieLiteralExpr(i + 1)); BoogieStmtList thenBody = new BoogieStmtList(); FunctionDefinition funcDef = publicFuncDefs[i]; string callee = TransUtils.GetCanonicalFunctionName(funcDef, context); List <BoogieExpr> inputs = new List <BoogieExpr>() { new BoogieIdentifierExpr("this"), new BoogieIdentifierExpr("msgsender_MSG"), new BoogieIdentifierExpr("msgvalue_MSG"), }; foreach (VariableDeclaration param in funcDef.Parameters.Parameters) { string name = TransUtils.GetCanonicalLocalVariableName(param); inputs.Add(new BoogieIdentifierExpr(name)); if (param.TypeName is ArrayTypeName array) { thenBody.AddStatement(new BoogieCallCmd( "FreshRefGenerator", new List <BoogieExpr>(), new List <BoogieIdentifierExpr>() { new BoogieIdentifierExpr(name) })); } } List <BoogieIdentifierExpr> outputs = new List <BoogieIdentifierExpr>(); var retParamCount = 0; foreach (VariableDeclaration param in funcDef.ReturnParameters.Parameters) { //string name = "__ret" + funcDef.Name; string name = $"__ret_{retParamCount++}_" + funcDef.Name; if (!string.IsNullOrEmpty(param.Name)) { name = TransUtils.GetCanonicalLocalVariableName(param); } outputs.Add(new BoogieIdentifierExpr(name)); } if (context.TranslateFlags.InstrumentGas) { havocGas(thenBody); } BoogieCallCmd callCmd = new BoogieCallCmd(callee, inputs, outputs); thenBody.AddStatement(callCmd); BoogieStmtList elseBody = ifCmd == null ? null : BoogieStmtList.MakeSingletonStmtList(ifCmd); ifCmd = new BoogieIfCmd(guard, thenBody, elseBody); } return(ifCmd); }
public static Tuple <BoogieIfCmd, int> GeneratePartialChoiceBlock(List <ContractDefinition> contracts, TranslatorContext context, BoogieExpr thisExpr, int startingPoint, bool canRevert, BoogieIfCmd alternatives = null, Tuple <string, string> callBackTarget = null) { BoogieIfCmd ifCmd = alternatives; int j = startingPoint; foreach (var contract in contracts) { if (contract.ContractKind != EnumContractKind.CONTRACT) { continue; } HashSet <FunctionDefinition> funcDefs = context.GetVisibleFunctionsByContract(contract); List <FunctionDefinition> publicFuncDefs = new List <FunctionDefinition>(); foreach (FunctionDefinition funcDef in funcDefs) { if (funcDef.IsConstructor) { continue; } if (funcDef.IsFallback) { continue; //let us not call fallback directly in harness } if (context.TranslateFlags.PerformFunctionSlice && !context.TranslateFlags.SliceFunctions.Contains(funcDef)) { continue; } if (funcDef.Visibility == EnumVisibility.PUBLIC || funcDef.Visibility == EnumVisibility.EXTERNAL) { publicFuncDefs.Add(funcDef); } } for (int i = publicFuncDefs.Count - 1; i >= 0; --i) { j++; BoogieExpr guard = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, new BoogieIdentifierExpr("choice"), new BoogieLiteralExpr(j)); BoogieStmtList thenBody = new BoogieStmtList(); FunctionDefinition funcDef = publicFuncDefs[i]; string callee = TransUtils.GetCanonicalFunctionName(funcDef, context); List <BoogieExpr> inputs = new List <BoogieExpr>() { // let us just call back into the calling contract callBackTarget != null ? new BoogieIdentifierExpr(callBackTarget.Item1) : thisExpr, callBackTarget != null ? new BoogieIdentifierExpr(callBackTarget.Item2) : new BoogieIdentifierExpr("msgsender_MSG"), new BoogieIdentifierExpr("msgvalue_MSG"), }; var inpParamCount = 0; foreach (VariableDeclaration param in funcDef.Parameters.Parameters) { string name = $"__arg_{inpParamCount++}_" + funcDef.Name; if (!string.IsNullOrEmpty(param.Name)) { name = TransUtils.GetCanonicalLocalVariableName(param, context); } BoogieIdentifierExpr boogieVar = new BoogieIdentifierExpr(name); inputs.Add(boogieVar); if (param.TypeName is ArrayTypeName array) { thenBody.AddStatement(new BoogieCallCmd( "FreshRefGenerator", new List <BoogieExpr>(), new List <BoogieIdentifierExpr>() { new BoogieIdentifierExpr(name) })); } thenBody.AppendStmtList(constrainVarValues(context, param, boogieVar)); } List <BoogieIdentifierExpr> outputs = new List <BoogieIdentifierExpr>(); var retParamCount = 0; foreach (VariableDeclaration param in funcDef.ReturnParameters.Parameters) { string name = $"__ret_{retParamCount++}_" + funcDef.Name; if (!string.IsNullOrEmpty(param.Name)) { name = TransUtils.GetCanonicalLocalVariableName(param, context); } outputs.Add(new BoogieIdentifierExpr(name)); } if (funcDef.StateMutability.Equals(EnumStateMutability.PAYABLE)) { BoogieExpr assumeExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, new BoogieIdentifierExpr("msgvalue_MSG"), new BoogieLiteralExpr(BigInteger.Zero)); thenBody.AddStatement(new BoogieAssumeCmd(assumeExpr)); if (!canRevert) { thenBody.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 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 { 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); if (context.TranslateFlags.InstrumentGas) { var gasVar = new BoogieIdentifierExpr("gas"); BoogieBinaryOperation gasGuard = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, gasVar, new BoogieLiteralExpr(BigInteger.Zero)); BoogieIfCmd ifExpr = new BoogieIfCmd(gasGuard, thenBody, null); thenBody = new BoogieStmtList(); //thenBody.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, gasVar, new BoogieLiteralExpr(TranslatorContext.TX_GAS_COST)))); thenBody.AddStatement(new BoogieAssignCmd(gasVar, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.SUB, gasVar, new BoogieLiteralExpr(TranslatorContext.TX_GAS_COST)))); thenBody.AddStatement(ifExpr); } BoogieStmtList elseBody = ifCmd == null ? null : BoogieStmtList.MakeSingletonStmtList(ifCmd); ifCmd = new BoogieIfCmd(guard, thenBody, elseBody); } } return(new Tuple <BoogieIfCmd, int>(ifCmd, j)); }
public static BoogieExpr GetDefaultVal(TypeName type) { return(GetDefaultVal(TransUtils.GetBoogieTypeFromSolidityTypeName(type))); }