Remove low-level log functions.

This commit is contained in:
chriseth 2020-10-21 18:57:10 +02:00
parent c20beaf2c9
commit bfc8e26007
15 changed files with 61 additions and 295 deletions

View File

@ -3,6 +3,7 @@
Breaking Changes:
* Assembler: The artificial ASSIGNIMMUTABLE opcode and the corresponding builtin in the "EVM with object access" dialect of Yul take the base offset of the code to modify as additional argument.
* Code Generator: All arithmetic is checked by default. These checks can be disabled using ``unchecked { ... }``.
* General: Remove global functions ``log0``, ``log1``, ``log2``, ``log3`` and ``log4``.
* Type System: Unary negation can only be used on signed integers, not on unsigned integers.
* Type System: Disallow explicit conversions from negative literals and literals larger than ``type(uint160).max`` to ``address`` type.
* Parser: Exponentiation is right associative. ``a**b**c`` is parsed as ``a**(b**c)``.

View File

@ -17,3 +17,10 @@ and it does something else afterwards.
* Explicit conversions from negative literals and literals larger than ``type(uint160).max`` to ``address`` are now disallowed.
* Exponentiation is right associative, i.e., the expression ``a**b**c`` is parsed as ``a**(b**c)``.
Before 0.8.0, it was parsed as ``(a**b)**c``.
Syntactic Only Changes
======================
* The global functions ``log0``, ``log1``, ``log2``, ``log3`` and ``log4`` have been removed.
These are low-level functions that were largely unused. Their behaviour can be accessed from inline assembly.

View File

@ -126,37 +126,6 @@ The output of the above looks like the following (trimmed):
}
}
.. index:: ! log
Low-Level Interface to Logs
===========================
It is also possible to access the low-level interface to the logging
mechanism via the functions ``log0``, ``log1``, ``log2``, ``log3`` and ``log4``.
Each function ``logi`` takes ``i + 1`` parameter of type ``bytes32``, where the first
argument will be used for the data part of the log and the others
as topics. The event call above can be performed in the same way as
::
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.10 <0.9.0;
contract C {
function f() public payable {
uint256 _id = 0x420042;
log3(
bytes32(msg.value),
bytes32(0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20),
bytes32(uint256(msg.sender)),
bytes32(_id)
);
}
}
where the long hexadecimal number is equal to
``keccak256("Deposit(address,bytes32,uint256)")``, the signature of the event.
Additional Resources for Understanding Events
==============================================

View File

@ -45,11 +45,6 @@ int magicVariableToID(std::string const& _name)
else if (_name == "ecrecover") return -6;
else if (_name == "gasleft") return -7;
else if (_name == "keccak256") return -8;
else if (_name == "log0") return -10;
else if (_name == "log1") return -11;
else if (_name == "log2") return -12;
else if (_name == "log3") return -13;
else if (_name == "log4") return -14;
else if (_name == "msg") return -15;
else if (_name == "mulmod") return -16;
else if (_name == "now") return -17;
@ -83,11 +78,6 @@ inline vector<shared_ptr<MagicVariableDeclaration const>> constructMagicVariable
magicVarDecl("ecrecover", TypeProvider::function(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)),
magicVarDecl("gasleft", TypeProvider::function(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)),
magicVarDecl("keccak256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
magicVarDecl("log0", TypeProvider::function(strings{"bytes32"}, strings{}, FunctionType::Kind::Log0)),
magicVarDecl("log1", TypeProvider::function(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log1)),
magicVarDecl("log2", TypeProvider::function(strings{"bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log2)),
magicVarDecl("log3", TypeProvider::function(strings{"bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log3)),
magicVarDecl("log4", TypeProvider::function(strings{"bytes32", "bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log4)),
magicVarDecl("msg", TypeProvider::magic(MagicType::Kind::Message)),
magicVarDecl("mulmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::MulMod, false, StateMutability::Pure)),
magicVarDecl("now", TypeProvider::uint256()),

View File

@ -3044,11 +3044,6 @@ string FunctionType::richIdentifier() const
case Kind::ECRecover: id += "ecrecover"; break;
case Kind::SHA256: id += "sha256"; break;
case Kind::RIPEMD160: id += "ripemd160"; break;
case Kind::Log0: id += "log0"; break;
case Kind::Log1: id += "log1"; break;
case Kind::Log2: id += "log2"; break;
case Kind::Log3: id += "log3"; break;
case Kind::Log4: id += "log4"; break;
case Kind::GasLeft: id += "gasleft"; break;
case Kind::Event: id += "event"; break;
case Kind::SetGas: id += "setgas"; break;

View File

@ -1138,11 +1138,6 @@ public:
ECRecover, ///< CALL to special contract for ecrecover
SHA256, ///< CALL to special contract for sha256
RIPEMD160, ///< CALL to special contract for ripemd160
Log0,
Log1,
Log2,
Log3,
Log4,
Event, ///< syntactic sugar for LOG*
SetGas, ///< modify the default gas value for the function call
SetValue, ///< modify the default value transfer for the function call

View File

@ -851,26 +851,6 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}
break;
}
case FunctionType::Kind::Log0:
case FunctionType::Kind::Log1:
case FunctionType::Kind::Log2:
case FunctionType::Kind::Log3:
case FunctionType::Kind::Log4:
{
unsigned logNumber = static_cast<unsigned>(function.kind()) - static_cast<unsigned>(FunctionType::Kind::Log0);
for (unsigned arg = logNumber; arg > 0; --arg)
acceptAndConvert(*arguments[arg], *function.parameterTypes()[arg], true);
arguments.front()->accept(*this);
utils().fetchFreeMemoryPointer();
solAssert(function.parameterTypes().front()->isValueType(), "");
utils().packedEncode(
{arguments.front()->annotation().type},
{function.parameterTypes().front()}
);
utils().toSizeAfterFreeMemoryPointer();
m_context << logInstruction(logNumber);
break;
}
case FunctionType::Kind::Event:
{
_functionCall.expression().accept(*this);
@ -1415,11 +1395,6 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::BareStaticCall:
case FunctionType::Kind::Transfer:
case FunctionType::Kind::Log0:
case FunctionType::Kind::Log1:
case FunctionType::Kind::Log2:
case FunctionType::Kind::Log3:
case FunctionType::Kind::Log4:
case FunctionType::Kind::ECRecover:
case FunctionType::Kind::SHA256:
case FunctionType::Kind::RIPEMD160:

View File

@ -1329,34 +1329,6 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
define(_functionCall) << functions[functionType->kind()] << "(" << args << ")\n";
break;
}
case FunctionType::Kind::Log0:
case FunctionType::Kind::Log1:
case FunctionType::Kind::Log2:
case FunctionType::Kind::Log3:
case FunctionType::Kind::Log4:
{
unsigned logNumber = static_cast<unsigned>(functionType->kind()) - static_cast<unsigned>(FunctionType::Kind::Log0);
solAssert(arguments.size() == logNumber + 1, "");
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
string indexedArgs;
for (unsigned arg = 0; arg < logNumber; ++arg)
indexedArgs += ", " + expressionAsType(*arguments[arg + 1], *(parameterTypes[arg + 1]));
Whiskers templ(R"({
let <pos> := <freeMemory>
let <end> := <encode>(<pos>, <nonIndexedArgs>)
<log>(<pos>, sub(<end>, <pos>) <indexedArgs>)
})");
templ("pos", m_context.newYulVariable());
templ("end", m_context.newYulVariable());
templ("freeMemory", freeMemory());
templ("encode", abi.tupleEncoder({arguments.front()->annotation().type}, {parameterTypes.front()}));
templ("nonIndexedArgs", IRVariable(*arguments.front()).commaSeparatedList());
templ("log", "log" + to_string(logNumber));
templ("indexedArgs", indexedArgs);
m_code << templ.render();
break;
}
case FunctionType::Kind::Creation:
{
solAssert(!functionType->gasSet(), "Gas limit set for contract creation.");
@ -1854,11 +1826,6 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::BareStaticCall:
case FunctionType::Kind::Transfer:
case FunctionType::Kind::Log0:
case FunctionType::Kind::Log1:
case FunctionType::Kind::Log2:
case FunctionType::Kind::Log3:
case FunctionType::Kind::Log4:
case FunctionType::Kind::ECRecover:
case FunctionType::Kind::SHA256:
case FunctionType::Kind::RIPEMD160:

View File

@ -662,13 +662,8 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
case FunctionType::Kind::ArrayPop:
arrayPop(_funCall);
break;
case FunctionType::Kind::Log0:
case FunctionType::Kind::Log1:
case FunctionType::Kind::Log2:
case FunctionType::Kind::Log3:
case FunctionType::Kind::Log4:
case FunctionType::Kind::Event:
// These can be safely ignored.
// This can be safely ignored.
break;
case FunctionType::Kind::ObjectCreation:
visitObjectCreation(_funCall);

View File

@ -1177,139 +1177,6 @@ BOOST_AUTO_TEST_CASE(uncalled_blockhash)
)
}
BOOST_AUTO_TEST_CASE(log0)
{
char const* sourceCode = R"(
contract test {
function a() public {
log0(bytes32(uint256(1)));
}
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
callContractFunction("a()");
BOOST_REQUIRE_EQUAL(numLogs(), 1);
BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress);
BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(1)));
BOOST_CHECK_EQUAL(numLogTopics(0), 0);
)
}
BOOST_AUTO_TEST_CASE(log1)
{
char const* sourceCode = R"(
contract test {
function a() public {
log1(bytes32(uint256(1)), bytes32(uint256(2)));
}
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
callContractFunction("a()");
BOOST_REQUIRE_EQUAL(numLogs(), 1);
BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress);
BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(1)));
BOOST_REQUIRE_EQUAL(numLogTopics(0), 1);
BOOST_CHECK_EQUAL(logTopic(0, 0), h256(u256(2)));
)
}
BOOST_AUTO_TEST_CASE(log2)
{
char const* sourceCode = R"(
contract test {
function a() public {
log2(bytes32(uint256(1)), bytes32(uint256(2)), bytes32(uint256(3)));
}
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
callContractFunction("a()");
BOOST_REQUIRE_EQUAL(numLogs(), 1);
BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress);
BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(1)));
BOOST_REQUIRE_EQUAL(numLogTopics(0), 2);
for (unsigned i = 0; i < 2; ++i)
BOOST_CHECK_EQUAL(logTopic(0, i), h256(u256(i + 2)));
)
}
BOOST_AUTO_TEST_CASE(log3)
{
char const* sourceCode = R"(
contract test {
function a() public {
log3(bytes32(uint256(1)), bytes32(uint256(2)), bytes32(uint256(3)), bytes32(uint256(4)));
}
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
callContractFunction("a()");
BOOST_REQUIRE_EQUAL(numLogs(), 1);
BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress);
BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(1)));
BOOST_REQUIRE_EQUAL(numLogTopics(0), 3);
for (unsigned i = 0; i < 3; ++i)
BOOST_CHECK_EQUAL(logTopic(0, i), h256(u256(i + 2)));
)
}
BOOST_AUTO_TEST_CASE(log4)
{
char const* sourceCode = R"(
contract test {
function a() public {
log4(bytes32(uint256(1)), bytes32(uint256(2)), bytes32(uint256(3)), bytes32(uint256(4)), bytes32(uint256(5)));
}
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
callContractFunction("a()");
BOOST_REQUIRE_EQUAL(numLogs(), 1);
BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress);
BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(1)));
BOOST_REQUIRE_EQUAL(numLogTopics(0), 4);
for (unsigned i = 0; i < 4; ++i)
BOOST_CHECK_EQUAL(logTopic(0, i), h256(u256(i + 2)));
)
}
BOOST_AUTO_TEST_CASE(log_in_constructor)
{
char const* sourceCode = R"(
contract test {
constructor() {
log1(bytes32(uint256(1)), bytes32(uint256(2)));
}
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
BOOST_REQUIRE_EQUAL(numLogs(), 1);
BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress);
BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(1)));
BOOST_REQUIRE_EQUAL(numLogTopics(0), 1);
BOOST_CHECK_EQUAL(logTopic(0, 0), h256(u256(2)));
)
}
BOOST_AUTO_TEST_CASE(selfdestruct)
{
char const* sourceCode = R"(
@ -1883,7 +1750,12 @@ BOOST_AUTO_TEST_CASE(event)
function deposit(bytes32 _id, bool _manually) public payable {
if (_manually) {
bytes32 s = 0x19dacbf83c5de6658e14cbf7bcae5c15eca2eedecf1c66fbca928e4d351bea0f;
log3(bytes32(msg.value), s, bytes32(uint256(msg.sender)), _id);
uint value = msg.value;
address sender = msg.sender;
assembly {
mstore(0, value)
log3(0, 0x20, s, sender, _id)
}
} else {
emit Deposit(msg.sender, _id, msg.value);
}
@ -1937,6 +1809,31 @@ BOOST_AUTO_TEST_CASE(event_emit)
)
}
BOOST_AUTO_TEST_CASE(event_constructor)
{
char const* sourceCode = R"(
contract ClientReceipt {
event Deposit(address indexed _from, bytes32 indexed _id, uint _value);
constructor() {
emit Deposit(msg.sender, bytes32("abc"), 7);
}
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
BOOST_REQUIRE_EQUAL(numLogs(), 1);
BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress);
BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(7)));
BOOST_REQUIRE_EQUAL(numLogTopics(0), 3);
BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address,bytes32,uint256)")));
BOOST_CHECK_EQUAL(logTopic(0, 1), h256(m_sender, h256::AlignRight));
BOOST_CHECK_EQUAL(logTopic(0, 2), h256(string{"abc"}, h256::FromBinary, h256::AlignLeft));
)
}
BOOST_AUTO_TEST_CASE(event_no_arguments)
{
char const* sourceCode = R"(

View File

@ -1,40 +0,0 @@
pragma experimental SMTChecker;
contract C {
function f() external {
bytes32 t1 = bytes32(uint256(0x1234));
log0(t1);
log1(t1, t1);
log2(t1, t1, t1);
log3(t1, t1, t1, t1);
log4(t1, t1, t1, t1, t1);
}
function g_data() pure internal returns (bytes32) {
assert(true);
return bytes32(uint256(0x5678));
}
function g() external {
// To test that the function call is actually visited.
log0(g_data());
log1(g_data(), g_data());
log2(g_data(), g_data(), g_data());
log3(g_data(), g_data(), g_data(), g_data());
log4(g_data(), g_data(), g_data(), g_data(), g_data());
}
bool x = true;
function h_data() view internal returns (bytes32) {
assert(x);
}
function h() external {
// To test that the function call is actually visited.
x = false;
log0(h_data());
log1(h_data(), h_data());
log2(h_data(), h_data(), h_data());
log3(h_data(), h_data(), h_data(), h_data());
log4(h_data(), h_data(), h_data(), h_data(), h_data());
}
}
// ----
// Warning 6321: (655-662): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.
// Warning 6328: (668-677): CHC: Assertion violation happens here.

View File

@ -0,0 +1,15 @@
contract C {
function f() public {
log0;
log1;
log2;
log3;
log4;
}
}
// ----
// DeclarationError 7576: (38-42): Undeclared identifier.
// DeclarationError 7576: (46-50): Undeclared identifier.
// DeclarationError 7576: (54-58): Undeclared identifier.
// DeclarationError 7576: (62-66): Undeclared identifier.
// DeclarationError 7576: (70-74): Undeclared identifier.

View File

@ -6,4 +6,4 @@ contract c {
}
}
// ----
// DeclarationError 7576: (101-105): Undeclared identifier. Did you mean "log8", "log9", "log0", "log1", "log2", "log3" or "log4"?
// DeclarationError 7576: (101-105): Undeclared identifier. Did you mean "log8" or "log9"?

View File

@ -12,6 +12,6 @@ contract c {
// DeclarationError 7576: (52-53): Undeclared identifier.
// DeclarationError 7576: (56-60): Undeclared identifier. Did you mean "long"?
// DeclarationError 7576: (70-71): Undeclared identifier.
// DeclarationError 7576: (74-78): Undeclared identifier. Did you mean "long", "log0", "log1", "log2", "log3" or "log4"?
// DeclarationError 7576: (74-78): Undeclared identifier. Did you mean "long"?
// DeclarationError 7576: (88-89): Undeclared identifier.
// DeclarationError 7576: (92-96): Undeclared identifier. Did you mean "long"?

View File

@ -1,13 +1,13 @@
contract C {
function f() public pure {
(bool a,) = address(this).call(abi.encode(address(this).delegatecall, super));
(a,) = address(this).delegatecall(abi.encode(log0, tx, mulmod));
(a,) = address(this).delegatecall(abi.encode(block, tx, mulmod));
a;
}
}
// ----
// TypeError 2056: (94-120): This type cannot be encoded.
// TypeError 2056: (122-127): This type cannot be encoded.
// TypeError 2056: (184-188): This type cannot be encoded.
// TypeError 2056: (190-192): This type cannot be encoded.
// TypeError 2056: (194-200): This type cannot be encoded.
// TypeError 2056: (184-189): This type cannot be encoded.
// TypeError 2056: (191-193): This type cannot be encoded.
// TypeError 2056: (195-201): This type cannot be encoded.