diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp index 7cf00fc9ed41..7298fe440008 100644 --- a/libevmasm/ConstantOptimiser.cpp +++ b/libevmasm/ConstantOptimiser.cpp @@ -383,7 +383,7 @@ bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const { auto numExps = static_cast(count(_routine.begin(), _routine.end(), Instruction::EXP)); return combineGas( - simpleRunGas(_routine, m_params.evmVersion) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)), + simpleRunGas(_routine, m_params.evmVersion) + numExps * GasCosts::expByteGasInTVM, // Data gas for routine: Some bytes are zero, but we ignore them. bytesRequired(_routine, m_params.evmVersion) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas), 0 diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index e7e107fe1d31..6c2cfdb0ee11 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -20,6 +20,8 @@ #include +#include + using namespace solidity; using namespace solidity::util; using namespace solidity::evmasm; @@ -87,17 +89,11 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ case Instruction::MLOAD: case Instruction::MSTORE: gas = runGas(_item.instruction(), m_evmVersion); - gas += memoryGas(classes.find(Instruction::ADD, { - m_state->relativeStackElement(0), - classes.find(AssemblyItem(32)) - })); + gas += memoryGas(m_state->relativeStackElement(0), u256(32)); break; case Instruction::MSTORE8: gas = runGas(_item.instruction(), m_evmVersion); - gas += memoryGas(classes.find(Instruction::ADD, { - m_state->relativeStackElement(0), - classes.find(AssemblyItem(1)) - })); + gas += memoryGas(m_state->relativeStackElement(0), u256(1)); break; case Instruction::KECCAK256: gas = GasCosts::keccak256Gas; @@ -113,11 +109,18 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ break; case Instruction::MCOPY: { - GasConsumption memoryGasFromRead = memoryGas(-1, -2); - GasConsumption memoryGasFromWrite = memoryGas(0, -2); - gas = runGas(_item.instruction(), m_evmVersion); - gas += (memoryGasFromRead < memoryGasFromWrite ? memoryGasFromWrite : memoryGasFromRead); + ExpressionClasses::Id sizeExpression = m_state->relativeStackElement(-2); + if (!classes.knownZero(sizeExpression)) + { + u256 const* source = classes.knownConstant(m_state->relativeStackElement(-1)); + u256 const* destination = classes.knownConstant(m_state->relativeStackElement(0)); + u256 const* size = classes.knownConstant(sizeExpression); + if (!source || !destination || !size) + gas = GasConsumption::infinite(); + else + gas += memoryGas(bigint(std::max(*source, *destination)) + *size); + } gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2)); break; } @@ -192,6 +195,8 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ { gas = GasCosts::createGas; gas += memoryGas(-1, -2); + if (_item.instruction() == Instruction::CREATE2) + gas += wordGas(GasCosts::create2WordGasInTVM, m_state->relativeStackElement(-2)); } break; case Instruction::EXP: @@ -202,11 +207,11 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ { // Note: msb() counts from 0 and throws on 0 as input. unsigned const significantByteCount = (static_cast(boost::multiprecision::msb(*value)) + 1u + 7u) / 8u; - gas += GasCosts::expByteGas(m_evmVersion) * significantByteCount; + gas += GasCosts::expByteGasInTVM * significantByteCount; } } else - gas += GasCosts::expByteGas(m_evmVersion) * 32; + gas += GasCosts::expByteGasInTVM * 32; break; case Instruction::BALANCE: case Instruction::TOKENBALANCE: @@ -267,36 +272,53 @@ GasMeter::GasConsumption GasMeter::wordGas(u256 const& _multiplier, ExpressionCl u256 const* value = m_state->expressionClasses().knownConstant(_value); if (!value) return GasConsumption::infinite(); - return GasConsumption(_multiplier * ((*value + 31) / 32)); + bigint gas = bigint(_multiplier) * ((bigint(*value) + 31) / 32); + if (gas > std::numeric_limits::max()) + return GasConsumption::infinite(); + return GasConsumption(u256(gas)); } -GasMeter::GasConsumption GasMeter::memoryGas(ExpressionClasses::Id _position) +GasMeter::GasConsumption GasMeter::memoryGas(bigint const& _position) { - u256 const* value = m_state->expressionClasses().knownConstant(_position); - if (!value) + if ( + _position < 0 || + _position > GasCosts::memorySizeLimitInTVM || + bigint(m_largestMemoryAccess) > GasCosts::memorySizeLimitInTVM + ) return GasConsumption::infinite(); - if (*value < m_largestMemoryAccess) + u256 const value = u256(_position); + if (value < m_largestMemoryAccess) return GasConsumption(0); u256 previous = m_largestMemoryAccess; - m_largestMemoryAccess = *value; + m_largestMemoryAccess = value; auto memGas = [=](u256 const& pos) -> u256 { u256 size = (pos + 31) / 32; return GasCosts::memoryGas * size + size * size / GasCosts::quadCoeffDiv; }; - return memGas(*value) - memGas(previous); + return memGas(value) - memGas(previous); +} + +GasMeter::GasConsumption GasMeter::memoryGas(ExpressionClasses::Id _offset, u256 const& _size) +{ + u256 const* offset = m_state->expressionClasses().knownConstant(_offset); + if (!offset) + return GasConsumption::infinite(); + return memoryGas(bigint(*offset) + _size); } GasMeter::GasConsumption GasMeter::memoryGas(int _stackPosOffset, int _stackPosSize) { ExpressionClasses& classes = m_state->expressionClasses(); - if (classes.knownZero(m_state->relativeStackElement(_stackPosSize))) + ExpressionClasses::Id offsetExpression = m_state->relativeStackElement(_stackPosOffset); + ExpressionClasses::Id sizeExpression = m_state->relativeStackElement(_stackPosSize); + if (classes.knownZero(sizeExpression)) return GasConsumption(0); - else - return memoryGas(classes.find(Instruction::ADD, { - m_state->relativeStackElement(_stackPosOffset), - m_state->relativeStackElement(_stackPosSize) - })); + u256 const* offset = classes.knownConstant(offsetExpression); + u256 const* size = classes.knownConstant(sizeExpression); + if (!offset || !size) + return GasConsumption::infinite(); + return memoryGas(bigint(*offset) + *size); } GasMeter::GasConsumption GasMeter::memoryGasForWordArray(int _stackPosOffset, int _stackPosElementCount) @@ -304,18 +326,12 @@ GasMeter::GasConsumption GasMeter::memoryGasForWordArray(int _stackPosOffset, in ExpressionClasses& classes = m_state->expressionClasses(); // The TVM reads and charges the 32-byte length slot even for empty arrays, // so unlike memoryGas(int, int) there is no zero-size shortcut here. - ExpressionClasses::Id byteSize = classes.find(Instruction::MUL, { - m_state->relativeStackElement(_stackPosElementCount), - classes.find(u256(32)) - }); - ExpressionClasses::Id byteSizeWithLengthSlot = classes.find(Instruction::ADD, { - byteSize, - classes.find(u256(32)) - }); - return memoryGas(classes.find(Instruction::ADD, { - m_state->relativeStackElement(_stackPosOffset), - byteSizeWithLengthSlot - })); + u256 const* offset = classes.knownConstant(m_state->relativeStackElement(_stackPosOffset)); + u256 const* elementCount = classes.knownConstant(m_state->relativeStackElement(_stackPosElementCount)); + if (!offset || !elementCount) + return GasConsumption::infinite(); + bigint const byteSizeWithLengthSlot = bigint(*elementCount) * 32 + 32; + return memoryGas(bigint(*offset) + byteSizeWithLengthSlot); } namespace diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h index 06a4b8b609de..3d9448104b28 100644 --- a/libevmasm/GasMeter.h +++ b/libevmasm/GasMeter.h @@ -190,6 +190,9 @@ namespace GasCosts static unsigned const extCodeHashGasInTVM = 400; static unsigned const callGasInTVM = 40; static unsigned const selfdestructGasInTVM = 5000; + static unsigned const expByteGasInTVM = 10; + static unsigned const create2WordGasInTVM = 6; + static unsigned const memorySizeLimitInTVM = 3 * 1024 * 1024; static unsigned const freezeV1GasInTVM = 20000; static unsigned const freezeExpireTimeGasInTVM = 50; @@ -267,11 +270,13 @@ class GasMeter static u256 dataGas(uint64_t _length, bool _inCreation, langutil::EVMVersion _evmVersion); private: - /// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise. + /// @returns _multiplier * ceil(_value / 32), if _value is a known constant and infinite otherwise. GasConsumption wordGas(u256 const& _multiplier, ExpressionClasses::Id _value); - /// @returns the gas needed to access the given memory position. + /// @returns the gas needed to access the given memory end position. /// @todo this assumes that memory was never accessed before and thus over-estimates gas usage. - GasConsumption memoryGas(ExpressionClasses::Id _position); + GasConsumption memoryGas(bigint const& _position); + /// @returns the memory gas for a known-size access starting at an offset on the stack. + GasConsumption memoryGas(ExpressionClasses::Id _offset, u256 const& _size); /// @returns the memory gas for accessing the memory at a specific offset for a number of bytes /// given as values on the stack at the given relative positions. GasConsumption memoryGas(int _stackPosOffset, int _stackPosSize); diff --git a/libsolidity/formal/Predicate.cpp b/libsolidity/formal/Predicate.cpp index 7f742ec18d0b..10854c3ab8ef 100644 --- a/libsolidity/formal/Predicate.cpp +++ b/libsolidity/formal/Predicate.cpp @@ -253,7 +253,12 @@ std::string Predicate::formatSummaryCall( if (magicKind == MagicType::Kind::Block && memberName == "difficulty") memberName = "prevrandao"; - if (magicKind == MagicType::Kind::Block || magicKind == MagicType::Kind::Message || magicKind == MagicType::Kind::Transaction) + if ( + magicKind == MagicType::Kind::Block || + magicKind == MagicType::Kind::Chain || + magicKind == MagicType::Kind::Message || + magicKind == MagicType::Kind::Transaction + ) txVars.insert(magicType->toString(true) + "." + memberName); } return true; diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index ad4120839a92..d4fbc73c734b 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -741,6 +741,8 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) // not modeled explicitly, conservatively invalidate the symbolic blockchain // state so balances and other observable state cannot remain falsely stable. state().newState(); + if (!funType.returnParameterTypes().empty()) + setSymbolicUnknownValue(*m_context.expression(_funCall), m_context); m_unsupportedErrors.warning( 4588_error, _funCall.location(), @@ -1470,7 +1472,7 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess) if (auto const* identifier = dynamic_cast(&memberExpr)) { auto const& name = identifier->name(); - solAssert(name == "block" || name == "msg" || name == "tx", ""); + solAssert(name == "block" || name == "chain" || name == "msg" || name == "tx", ""); auto memberName = _memberAccess.memberName(); // TODO remove this for 0.9.0 diff --git a/libsolidity/formal/SymbolicState.cpp b/libsolidity/formal/SymbolicState.cpp index 492e0055b008..8e55922ae573 100644 --- a/libsolidity/formal/SymbolicState.cpp +++ b/libsolidity/formal/SymbolicState.cpp @@ -234,7 +234,14 @@ smtutil::Expression SymbolicState::txTypeConstraints() const smt::symbolicUnknownConstraints(m_tx.member("block.gaslimit"), TypeProvider::uint256()) && smt::symbolicUnknownConstraints(m_tx.member("block.number"), TypeProvider::uint256()) && smt::symbolicUnknownConstraints(m_tx.member("block.timestamp"), TypeProvider::uint256()) && + smt::symbolicUnknownConstraints(m_tx.member("chain.totalEnergyCurrentLimit"), TypeProvider::uint(64)) && + smt::symbolicUnknownConstraints(m_tx.member("chain.totalEnergyWeight"), TypeProvider::uint(64)) && + smt::symbolicUnknownConstraints(m_tx.member("chain.totalNetLimit"), TypeProvider::uint(64)) && + smt::symbolicUnknownConstraints(m_tx.member("chain.totalNetWeight"), TypeProvider::uint(64)) && + smt::symbolicUnknownConstraints(m_tx.member("chain.unfreezeDelayDays"), TypeProvider::uint(64)) && smt::symbolicUnknownConstraints(m_tx.member("msg.sender"), TypeProvider::address()) && + smt::symbolicUnknownConstraints(m_tx.member("msg.tokenid"), TypeProvider::trcToken()) && + smt::symbolicUnknownConstraints(m_tx.member("msg.tokenvalue"), TypeProvider::uint256()) && smt::symbolicUnknownConstraints(m_tx.member("msg.value"), TypeProvider::uint256()) && smt::symbolicUnknownConstraints(m_tx.member("tx.origin"), TypeProvider::address()) && smt::symbolicUnknownConstraints(m_tx.member("tx.gasprice"), TypeProvider::uint256()); @@ -242,12 +249,19 @@ smtutil::Expression SymbolicState::txTypeConstraints() const smtutil::Expression SymbolicState::txNonPayableConstraint() const { - return m_tx.member("msg.value") == 0; + return + m_tx.member("msg.value") == 0 && + m_tx.member("msg.tokenid") == 0 && + m_tx.member("msg.tokenvalue") == 0; } smtutil::Expression SymbolicState::txFunctionConstraints(FunctionDefinition const& _function) const { - smtutil::Expression conj = _function.isPayable() ? smtutil::Expression(true) : txNonPayableConstraint(); + // Library functions inherit the caller's transaction values through DELEGATECALL. + smtutil::Expression conj = + (_function.isPayable() || _function.libraryFunction()) ? + smtutil::Expression(true) : + txNonPayableConstraint(); if (_function.isPartOfExternalInterface()) { auto sig = TypeProvider::function(_function)->externalIdentifier(); diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index 11c92a83af40..4e04aba75eeb 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -682,9 +682,16 @@ std::map transactionMemberTypes() {"block.timestamp", TypeProvider::uint256()}, {"blobhash", TypeProvider::array(DataLocation::Memory, TypeProvider::uint256())}, {"blockhash", TypeProvider::array(DataLocation::Memory, TypeProvider::uint256())}, + {"chain.totalEnergyCurrentLimit", TypeProvider::uint(64)}, + {"chain.totalEnergyWeight", TypeProvider::uint(64)}, + {"chain.totalNetLimit", TypeProvider::uint(64)}, + {"chain.totalNetWeight", TypeProvider::uint(64)}, + {"chain.unfreezeDelayDays", TypeProvider::uint(64)}, {"msg.data", TypeProvider::bytesCalldata()}, {"msg.sender", TypeProvider::address()}, {"msg.sig", TypeProvider::fixedBytes(4)}, + {"msg.tokenid", TypeProvider::trcToken()}, + {"msg.tokenvalue", TypeProvider::uint256()}, {"msg.value", TypeProvider::uint256()}, {"tx.gasprice", TypeProvider::uint256()}, {"tx.origin", TypeProvider::address()} diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 117dc0b1a07b..c1e6ae0b8dab 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -400,7 +400,7 @@ Json formatImmutableReferences(std::map checkKeys(Json const& _input, std::set const& _keys, std::string const& _name) { - if (!_input.empty() && !_input.is_object()) + if (!_input.is_object()) return formatFatalError(Error::Type::JSONError, "\"" + _name + "\" must be an object"); for (auto const& [member, _]: _input.items()) @@ -526,7 +526,7 @@ std::optional checkMetadataKeys(Json const& _input) std::optional checkOutputSelection(Json const& _outputSelection) { - if (!_outputSelection.empty() && !_outputSelection.is_object()) + if (!_outputSelection.is_object()) return formatFatalError(Error::Type::JSONError, "\"settings.outputSelection\" must be an object"); for (auto const& [sourceName, sourceVal]: _outputSelection.items()) @@ -649,6 +649,8 @@ std::variant StandardCompiler::parseI if (auto result = checkRootKeys(_input)) return *result; + if (_input.contains("language") && !_input["language"].is_string()) + return formatFatalError(Error::Type::JSONError, "\"language\" must be a string."); ret.language = _input.value("language", ""); Json const& sources = _input.value("sources", Json()); @@ -772,31 +774,28 @@ std::variant StandardCompiler::parseI if (!auxInputs.empty()) { Json const& smtlib2Responses = auxInputs.value("smtlib2responses", Json::object()); - if (!smtlib2Responses.empty()) - { - if (!smtlib2Responses.is_object()) - return formatFatalError(Error::Type::JSONError, "\"auxiliaryInput.smtlib2responses\" must be an object."); + if (!smtlib2Responses.is_object()) + return formatFatalError(Error::Type::JSONError, "\"auxiliaryInput.smtlib2responses\" must be an object."); - for (auto const& [hashString, response]: smtlib2Responses.items()) + for (auto const& [hashString, response]: smtlib2Responses.items()) + { + util::h256 hash; + try { - util::h256 hash; - try - { - hash = util::h256(hashString); - } - catch (util::BadHexCharacter const&) - { - return formatFatalError(Error::Type::JSONError, "Invalid hex encoding of SMTLib2 auxiliary input."); - } + hash = util::h256(hashString); + } + catch (util::BadHexCharacter const&) + { + return formatFatalError(Error::Type::JSONError, "Invalid hex encoding of SMTLib2 auxiliary input."); + } - if (!response.is_string()) - return formatFatalError( - Error::Type::JSONError, - "\"smtlib2Responses." + hashString + "\" must be a string." - ); + if (!response.is_string()) + return formatFatalError( + Error::Type::JSONError, + "\"smtlib2Responses." + hashString + "\" must be a string." + ); - ret.smtLib2Responses[hash] = response.get(); - } + ret.smtLib2Responses[hash] = response.get(); } } @@ -872,7 +871,11 @@ std::variant StandardCompiler::parseI std::vector components; for (Json const& arrayValue: settings["debug"]["debugInfo"]) + { + if (!arrayValue.is_string()) + return formatFatalError(Error::Type::JSONError, "Every value in settings.debug.debugInfo must be a string."); components.push_back(arrayValue.get()); + } std::optional debugInfoSelection = DebugInfoSelection::fromComponents( components, diff --git a/libyul/backends/evm/EVMMetrics.cpp b/libyul/backends/evm/EVMMetrics.cpp index 57593fdc26da..1d4e2be5ec02 100644 --- a/libyul/backends/evm/EVMMetrics.cpp +++ b/libyul/backends/evm/EVMMetrics.cpp @@ -115,7 +115,7 @@ bigint GasMeterVisitor::singleByteDataGas() const void GasMeterVisitor::instructionCostsInternal(evmasm::Instruction _instruction) { if (_instruction == evmasm::Instruction::EXP) - m_runGas += evmasm::GasCosts::expGas + evmasm::GasCosts::expByteGas(m_dialect.evmVersion()); + m_runGas += evmasm::GasCosts::expGas + evmasm::GasCosts::expByteGasInTVM; else if (_instruction == evmasm::Instruction::KECCAK256) // Assumes that Keccak-256 is computed on a single word (rounded up). m_runGas += evmasm::GasCosts::keccak256Gas + evmasm::GasCosts::keccak256WordGas; diff --git a/test/libevmasm/GasMeter.cpp b/test/libevmasm/GasMeter.cpp index aeac86a2931e..53dadf325ac7 100644 --- a/test/libevmasm/GasMeter.cpp +++ b/test/libevmasm/GasMeter.cpp @@ -62,6 +62,12 @@ AssemblyItems zeroArguments(size_t _count) return AssemblyItems(_count, AssemblyItem{u256(0)}); } +u256 memoryExpansionCost(u256 const& _byteSize) +{ + u256 const wordCount = (_byteSize + 31) / 32; + return GasCosts::memoryGas * wordCount + wordCount * wordCount / GasCosts::quadCoeffDiv; +} + /// Feeds the four NATIVEVOTE arguments as constants (or CALLVALUE for an unknown /// element count) and returns the estimate for the NATIVEVOTE item itself. GasMeter::GasConsumption estimateVote( @@ -138,6 +144,145 @@ BOOST_AUTO_TEST_CASE(tvm_sstore_uses_java_tron_set_and_reset_prices) BOOST_CHECK_EQUAL(reset.value, u256(GasCosts::sstoreResetGasInTVM)); } +BOOST_AUTO_TEST_CASE(tvm_exp_uses_fixed_byte_price_for_all_evm_versions) +{ + for (EVMVersion const& evmVersion: EVMVersion::allVersions()) + { + GasMeter::GasConsumption zeroExponent = estimateInstruction( + Instruction::EXP, + {u256(0), u256(2)}, + true, + evmVersion + ); + BOOST_REQUIRE(!zeroExponent.isInfinite); + BOOST_CHECK_EQUAL(zeroExponent.value, u256(GasCosts::expGas)); + + GasMeter::GasConsumption oneByteExponent = estimateInstruction( + Instruction::EXP, + {u256(1), u256(2)}, + true, + evmVersion + ); + BOOST_REQUIRE(!oneByteExponent.isInfinite); + BOOST_CHECK_EQUAL( + oneByteExponent.value, + u256(GasCosts::expGas + GasCosts::expByteGasInTVM) + ); + + GasMeter::GasConsumption fullWidthExponent = estimateInstruction( + Instruction::EXP, + {u256(-1), u256(2)}, + true, + evmVersion + ); + BOOST_REQUIRE(!fullWidthExponent.isInfinite); + BOOST_CHECK_EQUAL( + fullWidthExponent.value, + u256(GasCosts::expGas + 32 * GasCosts::expByteGasInTVM) + ); + } + + GasMeter::GasConsumption unknownExponent = estimateInstruction( + Instruction::EXP, + {AssemblyItem(Instruction::CALLVALUE), AssemblyItem(u256(2))} + ); + BOOST_REQUIRE(!unknownExponent.isInfinite); + BOOST_CHECK_EQUAL( + unknownExponent.value, + u256(GasCosts::expGas + 32 * GasCosts::expByteGasInTVM) + ); +} + +BOOST_AUTO_TEST_CASE(tvm_create2_charges_hash_cost_per_init_code_word) +{ + for (unsigned size: {0u, 1u, 32u, 33u}) + { + GasMeter::GasConsumption create = estimateInstruction( + Instruction::CREATE, + {u256(size), u256(0), u256(0)}, + false + ); + GasMeter::GasConsumption create2 = estimateInstruction( + Instruction::CREATE2, + {u256(0), u256(size), u256(0), u256(0)}, + false + ); + BOOST_REQUIRE(!create.isInfinite); + BOOST_REQUIRE(!create2.isInfinite); + u256 const wordCount = (u256(size) + 31) / 32; + u256 const expectedCreateCost = GasCosts::createGas + memoryExpansionCost(size); + BOOST_CHECK_EQUAL(create.value, expectedCreateCost); + BOOST_CHECK_EQUAL( + create2.value, + expectedCreateCost + GasCosts::create2WordGasInTVM * wordCount + ); + } +} + +BOOST_AUTO_TEST_CASE(tvm_memory_limit_and_address_overflow_are_unbounded) +{ + u256 const memoryLimit = GasCosts::memorySizeLimitInTVM; + GasMeter::GasConsumption atLimit = estimateInstruction( + Instruction::MSTORE8, + {u256(1), memoryLimit - 1} + ); + BOOST_REQUIRE(!atLimit.isInfinite); + BOOST_CHECK_EQUAL( + atLimit.value, + u256(GasMeter::runGas(Instruction::MSTORE8, EVMVersion{})) + memoryExpansionCost(memoryLimit) + ); + + GasMeter::GasConsumption aboveLimit = estimateInstruction( + Instruction::MSTORE8, + {u256(1), memoryLimit} + ); + BOOST_CHECK(aboveLimit.isInfinite); + + GasMeter::GasConsumption wrappedEnd = estimateInstruction( + Instruction::MSTORE8, + {u256(1), u256(-1)} + ); + BOOST_CHECK(wrappedEnd.isInfinite); + + // As in java-tron's memNeeded(), a zero-sized access does not expand memory, + // regardless of the offset value. + GasMeter::GasConsumption zeroSizeAtMaxOffset = estimateInstruction( + Instruction::RETURN, + {u256(0), u256(-1)} + ); + BOOST_REQUIRE(!zeroSizeAtMaxOffset.isInfinite); + BOOST_CHECK_EQUAL(zeroSizeAtMaxOffset.value, u256(0)); +} + +BOOST_AUTO_TEST_CASE(mcopy_charges_memory_expansion_to_the_larger_end) +{ + GasMeter::GasConsumption overlappingRanges = estimateInstruction( + Instruction::MCOPY, + {u256(64), u256(0), u256(32)}, + true, + EVMVersion::cancun() + ); + BOOST_REQUIRE(!overlappingRanges.isInfinite); + BOOST_CHECK_EQUAL( + overlappingRanges.value, + u256(GasMeter::runGas(Instruction::MCOPY, EVMVersion::cancun())) + + 2 * GasCosts::copyGas + + memoryExpansionCost(96) + ); + + GasMeter::GasConsumption zeroSizeAtMaxOffsets = estimateInstruction( + Instruction::MCOPY, + {u256(0), u256(-1), u256(-1)}, + true, + EVMVersion::cancun() + ); + BOOST_REQUIRE(!zeroSizeAtMaxOffsets.isInfinite); + BOOST_CHECK_EQUAL( + zeroSizeAtMaxOffsets.value, + u256(GasMeter::runGas(Instruction::MCOPY, EVMVersion::cancun())) + ); +} + BOOST_AUTO_TEST_CASE(tvm_call_family_uses_fixed_base_and_conditional_transfer_prices) { for (Instruction instruction: {Instruction::DELEGATECALL, Instruction::STATICCALL}) @@ -233,6 +378,28 @@ BOOST_AUTO_TEST_CASE(nativevote_with_unknown_element_count_is_unbounded) BOOST_CHECK(gas.isInfinite); } +BOOST_AUTO_TEST_CASE(nativevote_checks_memory_limit_without_u256_wraparound) +{ + u256 const maxElementCount = GasCosts::memorySizeLimitInTVM / 32 - 1; + GasMeter::GasConsumption atLimit = estimateVote(u256(0), maxElementCount, u256(0), u256(0)); + BOOST_REQUIRE(!atLimit.isInfinite); + BOOST_CHECK_EQUAL( + atLimit.value, + u256(GasCosts::voteGasInTVM) + memoryExpansionCost(GasCosts::memorySizeLimitInTVM) + ); + + GasMeter::GasConsumption aboveLimit = estimateVote(u256(0), maxElementCount + 1, u256(0), u256(0)); + BOOST_CHECK(aboveLimit.isInfinite); + + GasMeter::GasConsumption wrappedProduct = estimateVote( + u256(0), + u256(1) << 251, + u256(0), + u256(0) + ); + BOOST_CHECK(wrappedProduct.isInfinite); +} + BOOST_AUTO_TEST_SUITE_END() } // end namespaces diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index b53205e959e5..a70c88aa4058 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include @@ -48,6 +50,21 @@ namespace solidity::frontend::test namespace { + class ComputeMethodProbe: private ComputeMethod + { + public: + static bigint gasNeededFor(AssemblyItems const& _routine, EVMVersion _evmVersion) + { + u256 value = 0; + Params params{/* isCreation = */ false, /* runs = */ 1, /* multiplicity = */ 0, _evmVersion}; + ComputeMethodProbe probe(params, value); + return probe.ComputeMethod::gasNeeded(_routine); + } + + private: + ComputeMethodProbe(Params const& _params, u256 const& _value): ComputeMethod(_params, _value) {} + }; + AssemblyItems addDummyLocations(AssemblyItems const& _input) { // add dummy locations to each item so that we can check that they are not deleted @@ -157,6 +174,15 @@ namespace BOOST_AUTO_TEST_SUITE(Optimiser) +BOOST_AUTO_TEST_CASE(constant_optimizer_exp_uses_tvm_fixed_byte_cost) +{ + for (EVMVersion const& evmVersion: EVMVersion::allVersions()) + BOOST_CHECK_EQUAL( + ComputeMethodProbe::gasNeededFor({Instruction::EXP}, evmVersion), + GasCosts::expGas + GasCosts::expByteGasInTVM + ); +} + BOOST_AUTO_TEST_CASE(cse_push_immutable_same) { AssemblyItem pushImmutable{PushImmutable, 0x1234}; diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 34b268b75616..0a86bb40b652 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -323,6 +323,78 @@ BOOST_AUTO_TEST_CASE(assume_object_input) BOOST_CHECK(!containsAtMostWarnings(result)); } +BOOST_AUTO_TEST_CASE(settings_must_be_an_object) +{ + frontend::StandardCompiler compiler; + for (Json const& invalidSettings: {Json(nullptr), Json::array()}) + { + Json input = SolidityCode().json(); + input["settings"] = invalidSettings; + Json result = compiler.compile(input); + BOOST_CHECK(containsError(result, "JSONError", "\"settings\" must be an object")); + } +} + +BOOST_AUTO_TEST_CASE(metadata_settings_must_be_an_object) +{ + frontend::StandardCompiler compiler; + for (Json const& invalidMetadataSettings: {Json(nullptr), Json::array()}) + { + Json input = SolidityCode().json(); + input["settings"]["metadata"] = invalidMetadataSettings; + Json result = compiler.compile(input); + BOOST_CHECK(containsError(result, "JSONError", "\"settings.metadata\" must be an object")); + } +} + +BOOST_AUTO_TEST_CASE(output_selection_must_be_an_object) +{ + frontend::StandardCompiler compiler; + for (Json const& invalidOutputSelection: {Json(nullptr), Json::array()}) + { + Json input = SolidityCode().json(); + input["settings"]["outputSelection"] = invalidOutputSelection; + Json result = compiler.compile(input); + BOOST_CHECK(containsError(result, "JSONError", "\"settings.outputSelection\" must be an object")); + } +} + +BOOST_AUTO_TEST_CASE(language_must_be_a_string) +{ + frontend::StandardCompiler compiler; + for (Json const& invalidLanguage: {Json(nullptr), Json::array()}) + { + Json input = SolidityCode().json(); + input["language"] = invalidLanguage; + Json result = compiler.compile(input); + BOOST_CHECK(containsError(result, "JSONError", "\"language\" must be a string.")); + } +} + +BOOST_AUTO_TEST_CASE(smtlib2responses_must_be_an_object) +{ + frontend::StandardCompiler compiler; + for (Json const& invalidResponses: {Json(nullptr), Json::array()}) + { + Json input = SolidityCode().json(); + input["auxiliaryInput"]["smtlib2responses"] = invalidResponses; + Json result = compiler.compile(input); + BOOST_CHECK(containsError(result, "JSONError", "\"auxiliaryInput.smtlib2responses\" must be an object.")); + } +} + +BOOST_AUTO_TEST_CASE(debug_info_components_must_be_strings) +{ + frontend::StandardCompiler compiler; + for (Json const& invalidComponent: {Json(nullptr), Json(1)}) + { + Json input = SolidityCode().json(); + input["settings"]["debug"]["debugInfo"] = Json::array({invalidComponent}); + Json result = compiler.compile(input); + BOOST_CHECK(containsError(result, "JSONError", "Every value in settings.debug.debugInfo must be a string.")); + } +} + BOOST_AUTO_TEST_CASE(invalid_language) { char const* input = R"( @@ -528,9 +600,9 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(contract["evm"]["bytecode"]["object"].is_string()); BOOST_CHECK_EQUAL( solidity::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].get()), - std::string("6080604052348015600e575f5ffd5b5060") + + std::string("6080604052348015600e575f5ffd5b50d380156019575f5ffd5b50d280156024575f5ffd5b5060") + (VersionIsRelease ? "3e" : util::toHex(bytes{uint8_t(60 + VersionStringStrict.size())})) + - "80601a5f395ff3fe60806040525f5ffdfe" + "8060305f395ff3fe60806040525f5ffdfe" ); BOOST_CHECK(contract["evm"]["assembly"].is_string()); BOOST_CHECK(contract["evm"]["assembly"].get().find( @@ -538,10 +610,16 @@ BOOST_AUTO_TEST_CASE(basic_compilation) "callvalue\n dup1\n " "iszero\n tag_1\n jumpi\n " "revert(0x00, 0x00)\n" - "tag_1:\n pop\n dataSize(sub_0)\n dup1\n " + "tag_1:\n pop\n calltokenid\n dup1\n " + "iszero\n tag_2\n jumpi\n " + "revert(0x00, 0x00)\n" + "tag_2:\n pop\n calltokenvalue\n dup1\n " + "iszero\n tag_3\n jumpi\n " + "revert(0x00, 0x00)\n" + "tag_3:\n pop\n dataSize(sub_0)\n dup1\n " "dataOffset(sub_0)\n 0x00\n codecopy\n 0x00\n return\nstop\n\nsub_0: assembly {\n " "/* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n " - "revert(0x00, 0x00)\n\n auxdata: 0xa26469706673582212" + "revert(0x00, 0x00)\n\n auxdata: 0xa26474726f6e582212" ) == 0); BOOST_CHECK(contract["evm"]["gasEstimates"].is_object()); BOOST_CHECK_EQUAL(contract["evm"]["gasEstimates"].size(), 1); @@ -575,6 +653,28 @@ BOOST_AUTO_TEST_CASE(basic_compilation) "{\"begin\":0,\"end\":14,\"name\":\"tag\",\"source\":0,\"value\":\"1\"}," "{\"begin\":0,\"end\":14,\"name\":\"JUMPDEST\",\"source\":0}," "{\"begin\":0,\"end\":14,\"name\":\"POP\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"CALLTOKENID\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"DUP1\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"ISZERO\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"PUSH [tag]\",\"source\":0,\"value\":\"2\"}," + "{\"begin\":0,\"end\":14,\"name\":\"JUMPI\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"}," + "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"}," + "{\"begin\":0,\"end\":14,\"name\":\"REVERT\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"tag\",\"source\":0,\"value\":\"2\"}," + "{\"begin\":0,\"end\":14,\"name\":\"JUMPDEST\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"POP\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"CALLTOKENVALUE\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"DUP1\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"ISZERO\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"PUSH [tag]\",\"source\":0,\"value\":\"3\"}," + "{\"begin\":0,\"end\":14,\"name\":\"JUMPI\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"}," + "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"}," + "{\"begin\":0,\"end\":14,\"name\":\"REVERT\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"tag\",\"source\":0,\"value\":\"3\"}," + "{\"begin\":0,\"end\":14,\"name\":\"JUMPDEST\",\"source\":0}," + "{\"begin\":0,\"end\":14,\"name\":\"POP\",\"source\":0}," "{\"begin\":0,\"end\":14,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," "{\"begin\":0,\"end\":14,\"name\":\"DUP1\",\"source\":0}," "{\"begin\":0,\"end\":14,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," diff --git a/test/libsolidity/smtCheckerTests/special/ether_units.sol b/test/libsolidity/smtCheckerTests/special/ether_units.sol deleted file mode 100644 index 9c0e8ea71ff0..000000000000 --- a/test/libsolidity/smtCheckerTests/special/ether_units.sol +++ /dev/null @@ -1,17 +0,0 @@ -contract D { - function f() public pure { - assert(1000000000000000000 wei == 1 ether); - assert(100000000000000000 wei == 1 ether); - assert(1000000000 wei == 1 gwei); - assert(100000000 wei == 1 gwei); - assert(1000000000 gwei == 1 ether); - assert(100000000 gwei == 1 ether); - } -} -// ==== -// SMTEngine: all -// ---- -// Warning 6328: (89-130): CHC: Assertion violation happens here. -// Warning 6328: (170-201): CHC: Assertion violation happens here. -// Warning 6328: (243-276): CHC: Assertion violation happens here. -// Info 1391: CHC: 3 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/special/trx_units.sol b/test/libsolidity/smtCheckerTests/special/trx_units.sol new file mode 100644 index 000000000000..3f0d75021d14 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/trx_units.sol @@ -0,0 +1,17 @@ +contract D { + function f() public pure { + assert(1000000 sun == 1 trx); + assert(100000 sun == 1 trx); + assert(1 sun == 1); + assert(2 sun == 1); + assert(2 trx == 2000000 sun); + assert(2 trx == 200000 sun); + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (75-102): CHC: Assertion violation happens here. +// Warning 6328: (128-146): CHC: Assertion violation happens here. +// Warning 6328: (182-209): CHC: Assertion violation happens here. +// Info 1391: CHC: 3 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/tron/library_nonpayable_transaction_values.sol b/test/libsolidity/smtCheckerTests/tron/library_nonpayable_transaction_values.sol new file mode 100644 index 000000000000..f8e2e9cbdfd8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/tron/library_nonpayable_transaction_values.sol @@ -0,0 +1,14 @@ +library L { + function check() public view { + assert(msg.value == 0); + assert(msg.tokenvalue == 0); + assert(msg.tokenid == 0); + } +} +// ==== +// SMTEngine: chc +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (46-68): CHC: Assertion violation happens here. +// Warning 6328: (72-99): CHC: Assertion violation happens here. +// Warning 6328: (103-127): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/tron/library_nonpayable_transaction_values_bmc.sol b/test/libsolidity/smtCheckerTests/tron/library_nonpayable_transaction_values_bmc.sol new file mode 100644 index 000000000000..2f429cc51635 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/tron/library_nonpayable_transaction_values_bmc.sol @@ -0,0 +1,14 @@ +library L { + function check() public view { + assert(msg.value == 0); + assert(msg.tokenvalue == 0); + assert(msg.tokenid == 0); + } +} +// ==== +// SMTEngine: bmc +// SMTIgnoreCex: yes +// ---- +// Warning 4661: (46-68): BMC: Assertion violation happens here. +// Warning 4661: (72-99): BMC: Assertion violation happens here. +// Warning 4661: (103-127): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/tron/magic_members.sol b/test/libsolidity/smtCheckerTests/tron/magic_members.sol new file mode 100644 index 000000000000..2490de027d66 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/tron/magic_members.sol @@ -0,0 +1,39 @@ +contract C { + function chainParameters() external view { + assert(chain.totalNetLimit <= type(uint64).max); + assert(chain.totalNetWeight <= type(uint64).max); + assert(chain.totalEnergyCurrentLimit <= type(uint64).max); + assert(chain.totalEnergyWeight <= type(uint64).max); + assert(chain.unfreezeDelayDays <= type(uint64).max); + } + + function tokenCallParameterRanges() external payable { + assert(msg.tokenvalue <= type(uint256).max); + assert(msg.tokenid <= type(trcToken).max); + } + + function nonPayableTokenCallParameters() external view { + (uint256 trxValue, uint256 tokenValue, trcToken tokenId) = readTokenCallParameters(); + assert(trxValue == 0); + assert(tokenValue == 0); + assert(tokenId == 0); + } + + function readTokenCallParameters() internal view returns (uint256, uint256, trcToken) { + return (msg.value, msg.tokenvalue, msg.tokenid); + } +} +// ==== +// SMTEngine: chc +// SMTShowProvedSafe: yes +// ---- +// Info 9576: (59-106): CHC: Assertion violation check is safe! +// Info 9576: (110-158): CHC: Assertion violation check is safe! +// Info 9576: (162-219): CHC: Assertion violation check is safe! +// Info 9576: (223-274): CHC: Assertion violation check is safe! +// Info 9576: (278-329): CHC: Assertion violation check is safe! +// Info 9576: (393-436): CHC: Assertion violation check is safe! +// Info 9576: (440-481): CHC: Assertion violation check is safe! +// Info 9576: (635-656): CHC: Assertion violation check is safe! +// Info 9576: (660-683): CHC: Assertion violation check is safe! +// Info 9576: (687-707): CHC: Assertion violation check is safe! diff --git a/test/libsolidity/smtCheckerTests/tron/magic_members_bmc.sol b/test/libsolidity/smtCheckerTests/tron/magic_members_bmc.sol new file mode 100644 index 000000000000..c53d218872a0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/tron/magic_members_bmc.sol @@ -0,0 +1,39 @@ +contract C { + function chainParameters() external view { + assert(chain.totalNetLimit <= type(uint64).max); + assert(chain.totalNetWeight <= type(uint64).max); + assert(chain.totalEnergyCurrentLimit <= type(uint64).max); + assert(chain.totalEnergyWeight <= type(uint64).max); + assert(chain.unfreezeDelayDays <= type(uint64).max); + } + + function tokenCallParameterRanges() external payable { + assert(msg.tokenvalue <= type(uint256).max); + assert(msg.tokenid <= type(trcToken).max); + } + + function nonPayableTokenCallParameters() external view { + (uint256 trxValue, uint256 tokenValue, trcToken tokenId) = readTokenCallParameters(); + assert(trxValue == 0); + assert(tokenValue == 0); + assert(tokenId == 0); + } + + function readTokenCallParameters() internal view returns (uint256, uint256, trcToken) { + return (msg.value, msg.tokenvalue, msg.tokenid); + } +} +// ==== +// SMTEngine: bmc +// SMTShowProvedSafe: yes +// ---- +// Info 2961: (59-106): BMC: Assertion violation check is safe! +// Info 2961: (110-158): BMC: Assertion violation check is safe! +// Info 2961: (162-219): BMC: Assertion violation check is safe! +// Info 2961: (223-274): BMC: Assertion violation check is safe! +// Info 2961: (278-329): BMC: Assertion violation check is safe! +// Info 2961: (393-436): BMC: Assertion violation check is safe! +// Info 2961: (440-481): BMC: Assertion violation check is safe! +// Info 2961: (635-656): BMC: Assertion violation check is safe! +// Info 2961: (660-683): BMC: Assertion violation check is safe! +// Info 2961: (687-707): BMC: Assertion violation check is safe! diff --git a/test/libsolidity/smtCheckerTests/tron/state_mutation_return_ranges.sol b/test/libsolidity/smtCheckerTests/tron/state_mutation_return_ranges.sol new file mode 100644 index 000000000000..85a11e082ab8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/tron/state_mutation_return_ranges.sol @@ -0,0 +1,23 @@ +contract C { + function withdrawReward() external { + assert(withdrawreward() <= type(uint256).max); + } + + function cancelAllUnfreezeV2() external { + assert(cancelallunfreezev2() <= type(uint256).max); + } + + function withdrawExpireUnfreeze() external { + assert(withdrawexpireunfreeze() <= type(uint256).max); + } +} +// ==== +// SMTEngine: chc +// SMTShowProvedSafe: yes +// ---- +// Warning 4588: (60-76): Assertion checker does not yet implement this type of function call. Its state effects are modeled conservatively. +// Warning 4588: (156-177): Assertion checker does not yet implement this type of function call. Its state effects are modeled conservatively. +// Warning 4588: (260-284): Assertion checker does not yet implement this type of function call. Its state effects are modeled conservatively. +// Info 9576: (53-98): CHC: Assertion violation check is safe! +// Info 9576: (149-199): CHC: Assertion violation check is safe! +// Info 9576: (253-306): CHC: Assertion violation check is safe! diff --git a/test/libsolidity/smtCheckerTests/tron/state_mutation_return_ranges_bmc.sol b/test/libsolidity/smtCheckerTests/tron/state_mutation_return_ranges_bmc.sol new file mode 100644 index 000000000000..e1f83a097835 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/tron/state_mutation_return_ranges_bmc.sol @@ -0,0 +1,23 @@ +contract C { + function withdrawReward() external { + assert(withdrawreward() <= type(uint256).max); + } + + function cancelAllUnfreezeV2() external { + assert(cancelallunfreezev2() <= type(uint256).max); + } + + function withdrawExpireUnfreeze() external { + assert(withdrawexpireunfreeze() <= type(uint256).max); + } +} +// ==== +// SMTEngine: bmc +// SMTShowProvedSafe: yes +// ---- +// Warning 4588: (60-76): Assertion checker does not yet implement this type of function call. Its state effects are modeled conservatively. +// Warning 4588: (156-177): Assertion checker does not yet implement this type of function call. Its state effects are modeled conservatively. +// Warning 4588: (260-284): Assertion checker does not yet implement this type of function call. Its state effects are modeled conservatively. +// Info 2961: (53-98): BMC: Assertion violation check is safe! +// Info 2961: (149-199): BMC: Assertion violation check is safe! +// Info 2961: (253-306): BMC: Assertion violation check is safe! diff --git a/test/libsolidity/smtCheckerTests/types/address_balance.sol b/test/libsolidity/smtCheckerTests/types/address_balance.sol index a24fa32b18d2..3ca116ffb792 100644 --- a/test/libsolidity/smtCheckerTests/types/address_balance.sol +++ b/test/libsolidity/smtCheckerTests/types/address_balance.sol @@ -1,7 +1,7 @@ contract C { function f(address a, address b) public view { - uint x = b.balance + 1000 ether; + uint x = b.balance + 1000 trx; assert(a.balance > b.balance); } } @@ -10,5 +10,5 @@ contract C // SMTIgnoreCex: yes // ---- // Warning 2072: (63-69): Unused local variable. -// Warning 4984: (72-94): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. -// Warning 6328: (98-127): CHC: Assertion violation happens here. +// Warning 4984: (72-92): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (96-125): CHC: Assertion violation happens here. diff --git a/test/libyul/Metrics.cpp b/test/libyul/Metrics.cpp index 9e74cd150e49..ce97b0d66bd3 100644 --- a/test/libyul/Metrics.cpp +++ b/test/libyul/Metrics.cpp @@ -25,10 +25,14 @@ #include #include +#include +#include #include #include #include +#include + #include using namespace solidity::langutil; @@ -384,4 +388,21 @@ BOOST_FIXTURE_TEST_CASE(switch_statement_large_custom_weights, CustomWeightFixtu BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(YulEVMMetrics) + +BOOST_AUTO_TEST_CASE(exp_uses_tvm_fixed_byte_cost) +{ + for (EVMVersion const& evmVersion: EVMVersion::allVersions()) + { + auto const [runCost, dataCost] = GasMeterVisitor::instructionCosts( + evmasm::Instruction::EXP, + EVMDialect::strictAssemblyForEVM(evmVersion, std::nullopt) + ); + BOOST_CHECK_EQUAL(runCost, evmasm::GasCosts::expGas + evmasm::GasCosts::expByteGasInTVM); + BOOST_CHECK_EQUAL(dataCost, evmasm::GasCosts::createDataGas); + } +} + +BOOST_AUTO_TEST_SUITE_END() + }