2020-08-13 12:00:33 +00:00
/*
This file is part of solidity .
solidity is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
solidity is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with solidity . If not , see < http : //www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
# include <libsolidity/formal/Predicate.h>
2020-08-19 11:53:16 +00:00
# include <libsolidity/formal/SMTEncoder.h>
2021-07-01 15:28:06 +00:00
# include <liblangutil/CharStreamProvider.h>
# include <liblangutil/CharStream.h>
2020-08-13 12:00:33 +00:00
# include <libsolidity/ast/AST.h>
2021-01-20 10:22:28 +00:00
# include <libsolidity/ast/TypeProvider.h>
2020-08-13 12:00:33 +00:00
# include <boost/algorithm/string/join.hpp>
2021-10-06 09:48:15 +00:00
# include <boost/algorithm/string.hpp>
2021-01-20 10:22:28 +00:00
# include <range/v3/view.hpp>
2020-08-13 12:00:33 +00:00
# include <utility>
using namespace std ;
2021-10-06 09:48:15 +00:00
using boost : : algorithm : : starts_with ;
2020-08-13 12:00:33 +00:00
using namespace solidity ;
using namespace solidity : : smtutil ;
using namespace solidity : : frontend ;
using namespace solidity : : frontend : : smt ;
map < string , Predicate > Predicate : : m_predicates ;
Predicate const * Predicate : : create (
SortPointer _sort ,
string _name ,
2020-09-16 09:47:54 +00:00
PredicateType _type ,
2020-08-13 12:00:33 +00:00
EncodingContext & _context ,
2021-01-22 10:42:40 +00:00
ASTNode const * _node ,
2021-01-21 18:04:34 +00:00
ContractDefinition const * _contractContext ,
vector < ScopeOpener const * > _scopeStack
2020-08-13 12:00:33 +00:00
)
{
2022-08-23 17:28:45 +00:00
smt : : SymbolicFunctionVariable predicate { _sort , std : : move ( _name ) , _context } ;
2020-08-13 12:00:33 +00:00
string functorName = predicate . currentName ( ) ;
solAssert ( ! m_predicates . count ( functorName ) , " " ) ;
return & m_predicates . emplace (
std : : piecewise_construct ,
std : : forward_as_tuple ( functorName ) ,
2022-08-23 17:28:45 +00:00
std : : forward_as_tuple ( std : : move ( predicate ) , _type , _node , _contractContext , std : : move ( _scopeStack ) )
2020-08-13 12:00:33 +00:00
) . first - > second ;
}
Predicate : : Predicate (
smt : : SymbolicFunctionVariable & & _predicate ,
2020-09-16 09:47:54 +00:00
PredicateType _type ,
2021-01-22 10:42:40 +00:00
ASTNode const * _node ,
2021-01-21 18:04:34 +00:00
ContractDefinition const * _contractContext ,
vector < ScopeOpener const * > _scopeStack
2020-08-13 12:00:33 +00:00
) :
2022-08-23 17:28:45 +00:00
m_predicate ( std : : move ( _predicate ) ) ,
2020-09-16 09:47:54 +00:00
m_type ( _type ) ,
2021-01-22 10:42:40 +00:00
m_node ( _node ) ,
2021-01-21 18:04:34 +00:00
m_contractContext ( _contractContext ) ,
m_scopeStack ( _scopeStack )
2020-08-13 12:00:33 +00:00
{
}
Predicate const * Predicate : : predicate ( string const & _name )
{
return & m_predicates . at ( _name ) ;
}
void Predicate : : reset ( )
{
m_predicates . clear ( ) ;
}
smtutil : : Expression Predicate : : operator ( ) ( vector < smtutil : : Expression > const & _args ) const
{
return m_predicate ( _args ) ;
}
smtutil : : Expression Predicate : : functor ( ) const
{
return m_predicate . currentFunctionValue ( ) ;
}
smtutil : : Expression Predicate : : functor ( unsigned _idx ) const
{
return m_predicate . functionValueAtIndex ( _idx ) ;
}
void Predicate : : newFunctor ( )
{
m_predicate . increaseIndex ( ) ;
}
2020-08-19 11:53:16 +00:00
ASTNode const * Predicate : : programNode ( ) const
{
2020-08-13 12:00:33 +00:00
return m_node ;
}
2020-08-19 11:53:16 +00:00
2021-01-21 18:04:34 +00:00
ContractDefinition const * Predicate : : contextContract ( ) const
{
return m_contractContext ;
}
2020-08-19 11:53:16 +00:00
ContractDefinition const * Predicate : : programContract ( ) const
{
if ( auto const * contract = dynamic_cast < ContractDefinition const * > ( m_node ) )
if ( ! contract - > constructor ( ) )
return contract ;
return nullptr ;
}
FunctionDefinition const * Predicate : : programFunction ( ) const
{
if ( auto const * contract = dynamic_cast < ContractDefinition const * > ( m_node ) )
{
if ( contract - > constructor ( ) )
return contract - > constructor ( ) ;
return nullptr ;
}
if ( auto const * fun = dynamic_cast < FunctionDefinition const * > ( m_node ) )
return fun ;
return nullptr ;
}
2021-01-11 21:17:32 +00:00
FunctionCall const * Predicate : : programFunctionCall ( ) const
{
return dynamic_cast < FunctionCall const * > ( m_node ) ;
}
2021-10-12 09:12:18 +00:00
VariableDeclaration const * Predicate : : programVariable ( ) const
{
return dynamic_cast < VariableDeclaration const * > ( m_node ) ;
}
2020-08-19 11:53:16 +00:00
optional < vector < VariableDeclaration const * > > Predicate : : stateVariables ( ) const
{
2021-01-22 10:42:40 +00:00
if ( m_contractContext )
return SMTEncoder : : stateVariablesIncludingInheritedAndPrivate ( * m_contractContext ) ;
2020-08-19 11:53:16 +00:00
return nullopt ;
}
bool Predicate : : isSummary ( ) const
{
2021-01-11 21:17:32 +00:00
return isFunctionSummary ( ) | |
isInternalCall ( ) | |
isExternalCallTrusted ( ) | |
isExternalCallUntrusted ( ) | |
isConstructorSummary ( ) ;
2021-01-06 15:34:16 +00:00
}
bool Predicate : : isFunctionSummary ( ) const
{
return m_type = = PredicateType : : FunctionSummary ;
}
2021-01-21 18:04:34 +00:00
bool Predicate : : isFunctionBlock ( ) const
{
return m_type = = PredicateType : : FunctionBlock ;
}
bool Predicate : : isFunctionErrorBlock ( ) const
{
return m_type = = PredicateType : : FunctionErrorBlock ;
}
2021-01-06 15:34:16 +00:00
bool Predicate : : isInternalCall ( ) const
{
return m_type = = PredicateType : : InternalCall ;
}
2021-01-11 21:17:32 +00:00
bool Predicate : : isExternalCallTrusted ( ) const
2021-01-06 15:34:16 +00:00
{
2021-01-11 21:17:32 +00:00
return m_type = = PredicateType : : ExternalCallTrusted ;
}
bool Predicate : : isExternalCallUntrusted ( ) const
{
return m_type = = PredicateType : : ExternalCallUntrusted ;
2020-08-19 11:53:16 +00:00
}
2021-01-06 10:55:42 +00:00
bool Predicate : : isConstructorSummary ( ) const
{
return m_type = = PredicateType : : ConstructorSummary ;
}
2020-08-19 11:53:16 +00:00
bool Predicate : : isInterface ( ) const
{
2020-11-09 15:37:08 +00:00
return m_type = = PredicateType : : Interface ;
2020-08-19 11:53:16 +00:00
}
2021-10-06 09:48:15 +00:00
bool Predicate : : isNondetInterface ( ) const
{
return m_type = = PredicateType : : NondetInterface ;
}
2021-07-01 15:28:06 +00:00
string Predicate : : formatSummaryCall (
vector < smtutil : : Expression > const & _args ,
2021-09-16 17:18:26 +00:00
langutil : : CharStreamProvider const & _charStreamProvider ,
bool _appendTxVars
2021-07-01 15:28:06 +00:00
) const
2020-08-19 11:53:16 +00:00
{
2021-01-11 21:17:32 +00:00
solAssert ( isSummary ( ) , " " ) ;
2021-10-12 09:12:18 +00:00
if ( programVariable ( ) )
return { } ;
2021-07-01 15:28:06 +00:00
if ( auto funCall = programFunctionCall ( ) )
{
if ( funCall - > location ( ) . hasText ( ) )
return string ( _charStreamProvider . charStream ( * funCall - > location ( ) . sourceName ) . text ( funCall - > location ( ) ) ) ;
else
return { } ;
}
2020-08-19 11:53:16 +00:00
2021-01-20 10:22:28 +00:00
/// The signature of a function summary predicate is: summary(error, this, abiFunctions, cryptoFunctions, txData, preBlockChainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars).
2021-09-16 17:18:26 +00:00
/// Here we are interested in preInputVars to format the function call.
2021-01-20 10:22:28 +00:00
2021-09-16 17:18:26 +00:00
string txModel ;
if ( _appendTxVars )
2021-01-20 10:22:28 +00:00
{
2021-09-16 17:18:26 +00:00
set < string > txVars ;
if ( isFunctionSummary ( ) )
{
solAssert ( programFunction ( ) , " " ) ;
if ( programFunction ( ) - > isPayable ( ) )
txVars . insert ( " msg.value " ) ;
}
else if ( isConstructorSummary ( ) )
{
FunctionDefinition const * fun = programFunction ( ) ;
if ( fun & & fun - > isPayable ( ) )
txVars . insert ( " msg.value " ) ;
}
struct TxVarsVisitor : public ASTConstVisitor
{
bool visit ( MemberAccess const & _memberAccess )
{
Expression const * memberExpr = SMTEncoder : : innermostTuple ( _memberAccess . expression ( ) ) ;
Type const * exprType = memberExpr - > annotation ( ) . type ;
solAssert ( exprType , " " ) ;
if ( exprType - > category ( ) = = Type : : Category : : Magic )
if ( auto const * identifier = dynamic_cast < Identifier const * > ( memberExpr ) )
{
ASTString const & name = identifier - > name ( ) ;
2023-01-19 13:06:23 +00:00
auto memberName = _memberAccess . memberName ( ) ;
// TODO remove this for 0.9.0
if ( name = = " block " & & memberName = = " difficulty " )
memberName = " prevrandao " ;
2021-09-16 17:18:26 +00:00
if ( name = = " block " | | name = = " msg " | | name = = " tx " )
2023-01-19 13:06:23 +00:00
txVars . insert ( name + " . " + memberName ) ;
2021-09-16 17:18:26 +00:00
}
return true ;
}
set < string > txVars ;
} txVarsVisitor ;
if ( auto fun = programFunction ( ) )
{
fun - > accept ( txVarsVisitor ) ;
txVars + = txVarsVisitor . txVars ;
}
// Here we are interested in txData from the summary predicate.
auto txValues = readTxVars ( _args . at ( 4 ) ) ;
vector < string > values ;
for ( auto const & _var : txVars )
if ( auto v = txValues . at ( _var ) )
values . push_back ( _var + " : " + * v ) ;
if ( ! values . empty ( ) )
txModel = " { " + boost : : algorithm : : join ( values , " , " ) + " } " ;
2021-01-20 10:22:28 +00:00
}
if ( auto contract = programContract ( ) )
2021-09-16 17:18:26 +00:00
return contract - > name ( ) + " .constructor() " + txModel ;
2021-01-20 10:22:28 +00:00
2020-08-19 11:53:16 +00:00
auto stateVars = stateVariables ( ) ;
solAssert ( stateVars . has_value ( ) , " " ) ;
auto const * fun = programFunction ( ) ;
solAssert ( fun , " " ) ;
2020-12-08 20:14:18 +00:00
auto first = _args . begin ( ) + 6 + static_cast < int > ( stateVars - > size ( ) ) ;
2020-10-19 11:21:05 +00:00
auto last = first + static_cast < int > ( fun - > parameters ( ) . size ( ) ) ;
2020-08-19 11:53:16 +00:00
solAssert ( first > = _args . begin ( ) & & first < = _args . end ( ) , " " ) ;
solAssert ( last > = _args . begin ( ) & & last < = _args . end ( ) , " " ) ;
2021-09-17 19:28:07 +00:00
auto inTypes = SMTEncoder : : replaceUserTypes ( FunctionType ( * fun ) . parameterTypes ( ) ) ;
2020-10-19 11:21:05 +00:00
vector < optional < string > > functionArgsCex = formatExpressions ( vector < smtutil : : Expression > ( first , last ) , inTypes ) ;
2020-08-19 11:53:16 +00:00
vector < string > functionArgs ;
auto const & params = fun - > parameters ( ) ;
solAssert ( params . size ( ) = = functionArgsCex . size ( ) , " " ) ;
for ( unsigned i = 0 ; i < params . size ( ) ; + + i )
2020-10-19 15:13:21 +00:00
if ( params . at ( i ) & & functionArgsCex . at ( i ) )
2020-10-19 11:21:05 +00:00
functionArgs . emplace_back ( * functionArgsCex . at ( i ) ) ;
2020-08-19 11:53:16 +00:00
else
functionArgs . emplace_back ( params [ i ] - > name ( ) ) ;
string fName = fun - > isConstructor ( ) ? " constructor " :
fun - > isFallback ( ) ? " fallback " :
fun - > isReceive ( ) ? " receive " :
fun - > name ( ) ;
2021-04-07 12:52:56 +00:00
string prefix ;
if ( fun - > isFree ( ) )
prefix = ! fun - > sourceUnitName ( ) . empty ( ) ? ( fun - > sourceUnitName ( ) + " : " ) : " " ;
else
{
solAssert ( fun - > annotation ( ) . contract , " " ) ;
prefix = fun - > annotation ( ) . contract - > name ( ) + " . " ;
}
2021-09-16 17:18:26 +00:00
return prefix + fName + " ( " + boost : : algorithm : : join ( functionArgs , " , " ) + " ) " + txModel ;
2020-08-19 11:53:16 +00:00
}
2020-08-19 16:38:56 +00:00
2020-10-19 11:21:05 +00:00
vector < optional < string > > Predicate : : summaryStateValues ( vector < smtutil : : Expression > const & _args ) const
2020-08-19 16:38:56 +00:00
{
2020-12-08 20:14:18 +00:00
/// The signature of a function summary predicate is: summary(error, this, abiFunctions, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars).
/// The signature of the summary predicate of a contract without constructor is: summary(error, this, abiFunctions, cryptoFunctions, txData, preBlockchainState, postBlockchainState, preStateVars, postStateVars).
2020-08-19 16:38:56 +00:00
/// Here we are interested in postStateVars.
auto stateVars = stateVariables ( ) ;
solAssert ( stateVars . has_value ( ) , " " ) ;
2020-10-19 11:21:05 +00:00
vector < smtutil : : Expression > : : const_iterator stateFirst ;
vector < smtutil : : Expression > : : const_iterator stateLast ;
2020-08-19 16:38:56 +00:00
if ( auto const * function = programFunction ( ) )
{
2020-12-08 20:14:18 +00:00
stateFirst = _args . begin ( ) + 6 + static_cast < int > ( stateVars - > size ( ) ) + static_cast < int > ( function - > parameters ( ) . size ( ) ) + 1 ;
2020-08-19 16:38:56 +00:00
stateLast = stateFirst + static_cast < int > ( stateVars - > size ( ) ) ;
}
else if ( programContract ( ) )
{
2020-12-08 20:14:18 +00:00
stateFirst = _args . begin ( ) + 7 + static_cast < int > ( stateVars - > size ( ) ) ;
2020-08-19 16:38:56 +00:00
stateLast = stateFirst + static_cast < int > ( stateVars - > size ( ) ) ;
}
2021-10-12 09:12:18 +00:00
else if ( programVariable ( ) )
return { } ;
2020-08-19 16:38:56 +00:00
else
solAssert ( false , " " ) ;
solAssert ( stateFirst > = _args . begin ( ) & & stateFirst < = _args . end ( ) , " " ) ;
solAssert ( stateLast > = _args . begin ( ) & & stateLast < = _args . end ( ) , " " ) ;
2020-10-19 11:21:05 +00:00
vector < smtutil : : Expression > stateArgs ( stateFirst , stateLast ) ;
2020-08-19 16:38:56 +00:00
solAssert ( stateArgs . size ( ) = = stateVars - > size ( ) , " " ) ;
2022-03-07 04:25:35 +00:00
auto stateTypes = util : : applyMap ( * stateVars , [ & ] ( auto const & _var ) { return _var - > type ( ) ; } ) ;
2020-10-19 11:21:05 +00:00
return formatExpressions ( stateArgs , stateTypes ) ;
2020-08-19 16:38:56 +00:00
}
2020-08-20 08:43:50 +00:00
2020-10-19 11:21:05 +00:00
vector < optional < string > > Predicate : : summaryPostInputValues ( vector < smtutil : : Expression > const & _args ) const
2020-08-20 08:43:50 +00:00
{
2020-12-08 20:14:18 +00:00
/// The signature of a function summary predicate is: summary(error, this, abiFunctions, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars).
2020-08-20 08:43:50 +00:00
/// Here we are interested in postInputVars.
auto const * function = programFunction ( ) ;
solAssert ( function , " " ) ;
auto stateVars = stateVariables ( ) ;
solAssert ( stateVars . has_value ( ) , " " ) ;
auto const & inParams = function - > parameters ( ) ;
2020-12-08 20:14:18 +00:00
auto first = _args . begin ( ) + 6 + static_cast < int > ( stateVars - > size ( ) ) * 2 + static_cast < int > ( inParams . size ( ) ) + 1 ;
2020-10-19 11:21:05 +00:00
auto last = first + static_cast < int > ( inParams . size ( ) ) ;
2020-08-20 08:43:50 +00:00
solAssert ( first > = _args . begin ( ) & & first < = _args . end ( ) , " " ) ;
solAssert ( last > = _args . begin ( ) & & last < = _args . end ( ) , " " ) ;
2020-10-19 11:21:05 +00:00
vector < smtutil : : Expression > inValues ( first , last ) ;
2020-08-20 08:43:50 +00:00
solAssert ( inValues . size ( ) = = inParams . size ( ) , " " ) ;
2021-09-17 19:28:07 +00:00
auto inTypes = SMTEncoder : : replaceUserTypes ( FunctionType ( * function ) . parameterTypes ( ) ) ;
2020-10-19 11:21:05 +00:00
return formatExpressions ( inValues , inTypes ) ;
2020-08-20 08:43:50 +00:00
}
2020-10-19 11:21:05 +00:00
vector < optional < string > > Predicate : : summaryPostOutputValues ( vector < smtutil : : Expression > const & _args ) const
2020-08-20 08:43:50 +00:00
{
2020-12-08 20:14:18 +00:00
/// The signature of a function summary predicate is: summary(error, this, abiFunctions, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars).
2020-08-20 08:43:50 +00:00
/// Here we are interested in outputVars.
auto const * function = programFunction ( ) ;
solAssert ( function , " " ) ;
auto stateVars = stateVariables ( ) ;
solAssert ( stateVars . has_value ( ) , " " ) ;
auto const & inParams = function - > parameters ( ) ;
2020-12-08 20:14:18 +00:00
auto first = _args . begin ( ) + 6 + static_cast < int > ( stateVars - > size ( ) ) * 2 + static_cast < int > ( inParams . size ( ) ) * 2 + 1 ;
2020-08-20 08:43:50 +00:00
solAssert ( first > = _args . begin ( ) & & first < = _args . end ( ) , " " ) ;
2020-10-19 11:21:05 +00:00
vector < smtutil : : Expression > outValues ( first , _args . end ( ) ) ;
2020-08-20 08:43:50 +00:00
solAssert ( outValues . size ( ) = = function - > returnParameters ( ) . size ( ) , " " ) ;
2021-09-17 19:28:07 +00:00
auto outTypes = SMTEncoder : : replaceUserTypes ( FunctionType ( * function ) . returnParameterTypes ( ) ) ;
2020-10-19 11:21:05 +00:00
return formatExpressions ( outValues , outTypes ) ;
}
2021-01-21 18:04:34 +00:00
pair < vector < optional < string > > , vector < VariableDeclaration const * > > Predicate : : localVariableValues ( vector < smtutil : : Expression > const & _args ) const
{
/// The signature of a local block predicate is:
/// block(error, this, abiFunctions, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars, localVars).
/// Here we are interested in localVars.
auto const * function = programFunction ( ) ;
solAssert ( function , " " ) ;
auto const & localVars = SMTEncoder : : localVariablesIncludingModifiers ( * function , m_contractContext ) ;
auto first = _args . end ( ) - static_cast < int > ( localVars . size ( ) ) ;
vector < smtutil : : Expression > outValues ( first , _args . end ( ) ) ;
2022-03-07 04:25:35 +00:00
auto mask = util : : applyMap (
2021-01-21 18:04:34 +00:00
localVars ,
[ this ] ( auto _var ) {
auto varScope = dynamic_cast < ScopeOpener const * > ( _var - > scope ( ) ) ;
return find ( begin ( m_scopeStack ) , end ( m_scopeStack ) , varScope ) ! = end ( m_scopeStack ) ;
}
) ;
auto localVarsInScope = util : : filter ( localVars , mask ) ;
auto outValuesInScope = util : : filter ( outValues , mask ) ;
2022-03-07 04:25:35 +00:00
auto outTypes = util : : applyMap ( localVarsInScope , [ ] ( auto _var ) { return _var - > type ( ) ; } ) ;
2021-01-21 18:04:34 +00:00
return { formatExpressions ( outValuesInScope , outTypes ) , localVarsInScope } ;
}
2021-10-06 09:48:15 +00:00
map < string , string > Predicate : : expressionSubstitution ( smtutil : : Expression const & _predExpr ) const
{
map < string , string > subst ;
string predName = functor ( ) . name ;
solAssert ( contextContract ( ) , " " ) ;
auto const & stateVars = SMTEncoder : : stateVariablesIncludingInheritedAndPrivate ( * contextContract ( ) ) ;
auto nArgs = _predExpr . arguments . size ( ) ;
// The signature of an interface predicate is
// interface(this, abiFunctions, cryptoFunctions, blockchainState, stateVariables).
// An invariant for an interface predicate is a contract
// invariant over its state, for example `x <= 0`.
if ( isInterface ( ) )
{
solAssert ( starts_with ( predName , " interface " ) , " " ) ;
subst [ _predExpr . arguments . at ( 0 ) . name ] = " address(this) " ;
solAssert ( nArgs = = stateVars . size ( ) + 4 , " " ) ;
for ( size_t i = nArgs - stateVars . size ( ) ; i < nArgs ; + + i )
subst [ _predExpr . arguments . at ( i ) . name ] = stateVars . at ( i - 4 ) - > name ( ) ;
}
// The signature of a nondet interface predicate is
// nondet_interface(error, this, abiFunctions, cryptoFunctions, blockchainState, stateVariables, blockchainState', stateVariables').
// An invariant for a nondet interface predicate is a reentrancy property
// over the pre and post state variables of a contract, where pre state vars
// are represented by the variable's name and post state vars are represented
// by the primed variable's name, for example
// `(x <= 0) => (x' <= 100)`.
else if ( isNondetInterface ( ) )
{
solAssert ( starts_with ( predName , " nondet_interface " ) , " " ) ;
subst [ _predExpr . arguments . at ( 0 ) . name ] = " <errorCode> " ;
subst [ _predExpr . arguments . at ( 1 ) . name ] = " address(this) " ;
solAssert ( nArgs = = stateVars . size ( ) * 2 + 6 , " " ) ;
for ( size_t i = nArgs - stateVars . size ( ) , s = 0 ; i < nArgs ; + + i , + + s )
subst [ _predExpr . arguments . at ( i ) . name ] = stateVars . at ( s ) - > name ( ) + " ' " ;
for ( size_t i = nArgs - ( stateVars . size ( ) * 2 + 1 ) , s = 0 ; i < nArgs - ( stateVars . size ( ) + 1 ) ; + + i , + + s )
subst [ _predExpr . arguments . at ( i ) . name ] = stateVars . at ( s ) - > name ( ) ;
}
return subst ;
}
2021-03-22 16:12:05 +00:00
vector < optional < string > > Predicate : : formatExpressions ( vector < smtutil : : Expression > const & _exprs , vector < Type const * > const & _types ) const
2020-10-19 11:21:05 +00:00
{
solAssert ( _exprs . size ( ) = = _types . size ( ) , " " ) ;
vector < optional < string > > strExprs ;
for ( unsigned i = 0 ; i < _exprs . size ( ) ; + + i )
strExprs . push_back ( expressionToString ( _exprs . at ( i ) , _types . at ( i ) ) ) ;
return strExprs ;
}
2021-03-22 16:12:05 +00:00
optional < string > Predicate : : expressionToString ( smtutil : : Expression const & _expr , Type const * _type ) const
2020-10-19 11:21:05 +00:00
{
if ( smt : : isNumber ( * _type ) )
{
solAssert ( _expr . sort - > kind = = Kind : : Int , " " ) ;
solAssert ( _expr . arguments . empty ( ) , " " ) ;
2021-09-16 17:18:26 +00:00
if (
_type - > category ( ) = = Type : : Category : : Address | |
_type - > category ( ) = = Type : : Category : : FixedBytes
)
{
try
{
if ( _expr . name = = " 0 " )
return " 0x0 " ;
// For some reason the code below returns "0x" for "0".
2022-03-07 04:25:35 +00:00
return util : : toHex ( toCompactBigEndian ( bigint ( _expr . name ) ) , util : : HexPrefix : : Add , util : : HexCase : : Lower ) ;
2021-09-16 17:18:26 +00:00
}
catch ( out_of_range const & )
{
}
catch ( invalid_argument const & )
{
}
}
2020-10-19 11:21:05 +00:00
return _expr . name ;
}
if ( smt : : isBool ( * _type ) )
{
solAssert ( _expr . sort - > kind = = Kind : : Bool , " " ) ;
solAssert ( _expr . arguments . empty ( ) , " " ) ;
solAssert ( _expr . name = = " true " | | _expr . name = = " false " , " " ) ;
return _expr . name ;
}
if ( smt : : isFunction ( * _type ) )
{
solAssert ( _expr . arguments . empty ( ) , " " ) ;
return _expr . name ;
}
2020-10-19 15:13:21 +00:00
if ( smt : : isArray ( * _type ) )
{
auto const & arrayType = dynamic_cast < ArrayType const & > ( * _type ) ;
2021-01-20 10:22:28 +00:00
if ( _expr . name ! = " tuple_constructor " )
return { } ;
2020-10-19 15:13:21 +00:00
auto const & tupleSort = dynamic_cast < TupleSort const & > ( * _expr . sort ) ;
solAssert ( tupleSort . components . size ( ) = = 2 , " " ) ;
2020-10-27 17:48:12 +00:00
2020-12-07 10:03:07 +00:00
unsigned long length ;
try
{
length = stoul ( _expr . arguments . at ( 1 ) . name ) ;
}
catch ( out_of_range const & )
{
return { } ;
}
2020-12-08 20:14:18 +00:00
catch ( invalid_argument const & )
{
return { } ;
}
2020-10-27 17:48:12 +00:00
// Limit this counterexample size to 1k.
// Some OSs give you "unlimited" memory through swap and other virtual memory,
// so purely relying on bad_alloc being thrown is not a good idea.
// In that case, the array allocation might cause OOM and the program is killed.
if ( length > = 1024 )
return { } ;
2020-10-19 15:13:21 +00:00
try
{
2020-10-27 17:48:12 +00:00
vector < string > array ( length ) ;
2020-10-19 15:13:21 +00:00
if ( ! fillArray ( _expr . arguments . at ( 0 ) , array , arrayType ) )
return { } ;
return " [ " + boost : : algorithm : : join ( array , " , " ) + " ] " ;
}
catch ( bad_alloc const & )
{
// Solver gave a concrete array but length is too large.
}
}
2020-12-07 10:03:07 +00:00
if ( smt : : isNonRecursiveStruct ( * _type ) )
{
auto const & structType = dynamic_cast < StructType const & > ( * _type ) ;
solAssert ( _expr . name = = " tuple_constructor " , " " ) ;
auto const & tupleSort = dynamic_cast < TupleSort const & > ( * _expr . sort ) ;
auto members = structType . structDefinition ( ) . members ( ) ;
solAssert ( tupleSort . components . size ( ) = = members . size ( ) , " " ) ;
solAssert ( _expr . arguments . size ( ) = = members . size ( ) , " " ) ;
vector < string > elements ;
for ( unsigned i = 0 ; i < members . size ( ) ; + + i )
{
optional < string > elementStr = expressionToString ( _expr . arguments . at ( i ) , members [ i ] - > type ( ) ) ;
elements . push_back ( members [ i ] - > name ( ) + ( elementStr . has_value ( ) ? " : " + elementStr . value ( ) : " " ) ) ;
}
return " { " + boost : : algorithm : : join ( elements , " , " ) + " } " ;
}
2020-10-19 11:21:05 +00:00
return { } ;
2020-08-20 08:43:50 +00:00
}
2020-10-19 15:13:21 +00:00
bool Predicate : : fillArray ( smtutil : : Expression const & _expr , vector < string > & _array , ArrayType const & _type ) const
{
// Base case
if ( _expr . name = = " const_array " )
{
auto length = _array . size ( ) ;
optional < string > elemStr = expressionToString ( _expr . arguments . at ( 1 ) , _type . baseType ( ) ) ;
if ( ! elemStr )
return false ;
_array . clear ( ) ;
_array . resize ( length , * elemStr ) ;
return true ;
}
// Recursive case.
if ( _expr . name = = " store " )
{
if ( ! fillArray ( _expr . arguments . at ( 0 ) , _array , _type ) )
return false ;
optional < string > indexStr = expressionToString ( _expr . arguments . at ( 1 ) , TypeProvider : : uint256 ( ) ) ;
if ( ! indexStr )
return false ;
// Sometimes the solver assigns huge lengths that are not related,
// we should catch and ignore those.
2020-07-13 20:40:33 +00:00
unsigned long index ;
2020-10-19 15:13:21 +00:00
try
{
index = stoul ( * indexStr ) ;
}
catch ( out_of_range const & )
{
return true ;
}
2020-12-08 20:14:18 +00:00
catch ( invalid_argument const & )
{
return true ;
}
2020-10-19 15:13:21 +00:00
optional < string > elemStr = expressionToString ( _expr . arguments . at ( 2 ) , _type . baseType ( ) ) ;
if ( ! elemStr )
return false ;
if ( index < _array . size ( ) )
_array . at ( index ) = * elemStr ;
return true ;
}
2020-12-07 10:03:07 +00:00
// Special base case, not supported yet.
if ( _expr . name . rfind ( " (_ as-array " ) = = 0 )
{
// Z3 expression representing reinterpretation of a different term as an array
return false ;
}
2020-10-19 15:13:21 +00:00
solAssert ( false , " " ) ;
}
2021-01-20 10:22:28 +00:00
map < string , optional < string > > Predicate : : readTxVars ( smtutil : : Expression const & _tx ) const
{
2021-03-22 16:12:05 +00:00
map < string , Type const * > const txVars {
2021-08-11 10:19:10 +00:00
{ " block.basefee " , TypeProvider : : uint256 ( ) } ,
2021-01-20 10:22:28 +00:00
{ " block.chainid " , TypeProvider : : uint256 ( ) } ,
{ " block.coinbase " , TypeProvider : : address ( ) } ,
2023-01-19 13:06:23 +00:00
{ " block.prevrandao " , TypeProvider : : uint256 ( ) } ,
2021-01-20 10:22:28 +00:00
{ " block.gaslimit " , TypeProvider : : uint256 ( ) } ,
{ " block.number " , TypeProvider : : uint256 ( ) } ,
{ " block.timestamp " , TypeProvider : : uint256 ( ) } ,
{ " blockhash " , TypeProvider : : array ( DataLocation : : Memory , TypeProvider : : uint256 ( ) ) } ,
2021-09-16 17:18:26 +00:00
{ " msg.data " , TypeProvider : : array ( DataLocation : : CallData ) } ,
2021-01-20 10:22:28 +00:00
{ " msg.sender " , TypeProvider : : address ( ) } ,
2021-09-16 17:18:26 +00:00
{ " msg.sig " , TypeProvider : : fixedBytes ( 4 ) } ,
2021-01-20 10:22:28 +00:00
{ " msg.value " , TypeProvider : : uint256 ( ) } ,
{ " tx.gasprice " , TypeProvider : : uint256 ( ) } ,
{ " tx.origin " , TypeProvider : : address ( ) }
} ;
map < string , optional < string > > vars ;
for ( auto & & [ i , v ] : txVars | ranges : : views : : enumerate )
vars . emplace ( v . first , expressionToString ( _tx . arguments . at ( i ) , v . second ) ) ;
return vars ;
}