From b44e87fb7a1281b9d8b33bb3732cf56be8518d2e Mon Sep 17 00:00:00 2001 From: Dima Litvinov Date: Sun, 28 Apr 2024 13:47:36 +0100 Subject: [PATCH] SKALED-1878 Colorful logs --- libethcore/ChainOperationParams.h | 1 + libethcore/Exceptions.h | 3 + libethereum/ChainParams.cpp | 2 + libethereum/ClientBase.cpp | 15 +--- libethereum/ClientBase.h | 1 - libethereum/Interface.h | 9 --- libskutils/include/skutils/console_colors.h | 9 +-- libskutils/src/console_colors.cpp | 8 +-- libweb3jsonrpc/Eth.cpp | 16 +++-- test/unittests/libweb3jsonrpc/jsonrpc.cpp | 77 ++++++++++++++++++++- 10 files changed, 105 insertions(+), 36 deletions(-) diff --git a/libethcore/ChainOperationParams.h b/libethcore/ChainOperationParams.h index 5d773e216..9123ad3cf 100644 --- a/libethcore/ChainOperationParams.h +++ b/libethcore/ChainOperationParams.h @@ -256,6 +256,7 @@ struct ChainOperationParams { u256 externalGasDifficulty = ~u256( 0 ); typedef std::vector< std::string > vecAdminOrigins_t; vecAdminOrigins_t vecAdminOrigins; // wildcard based folters for IP addresses + int getLogsBlocksLimit = -1; time_t getPatchTimestamp( SchainPatchEnum _patchEnum ) const; diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h index 60a76954c..a39c33a00 100644 --- a/libethcore/Exceptions.h +++ b/libethcore/Exceptions.h @@ -107,5 +107,8 @@ DEV_SIMPLE_EXCEPTION( FailedToDownloadDaoForkBlockHeader ); DEV_SIMPLE_EXCEPTION( AccountLocked ); DEV_SIMPLE_EXCEPTION( TransactionRefused ); DEV_SIMPLE_EXCEPTION( UnknownAccount ); + +DEV_SIMPLE_EXCEPTION( TooBigResponse ); + } // namespace eth } // namespace dev diff --git a/libethereum/ChainParams.cpp b/libethereum/ChainParams.cpp index ca4f51b6b..cb1b90d40 100644 --- a/libethereum/ChainParams.cpp +++ b/libethereum/ChainParams.cpp @@ -106,6 +106,8 @@ ChainParams ChainParams::loadConfig( cp.skaleDisableChainIdCheck = params.count( c_skaleDisableChainIdCheck ) ? params[c_skaleDisableChainIdCheck].get_bool() : false; + cp.getLogsBlocksLimit = + params.count( "getLogsBlocksLimit" ) ? params.at( "getLogsBlocksLimit" ).get_int() : -1; if ( obj.count( c_skaleConfig ) ) { processSkaleConfigItems( cp, obj ); diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index bc88f0e34..b0367ff78 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -219,6 +219,9 @@ LocalisedLogEntries ClientBase::logs( LogFilter const& _f ) const { unsigned begin = min( bc().number() + 1, ( unsigned ) _f.latest() ); unsigned end = min( bc().number(), min( begin, ( unsigned ) _f.earliest() ) ); + if ( begin >= end && begin - end > bc().chainParams().getLogsBlocksLimit ) + BOOST_THROW_EXCEPTION( TooBigResponse() ); + // Handle pending transactions differently as they're not on the block chain. if ( begin > bc().number() ) { Block temp = postSeal(); @@ -350,18 +353,6 @@ bool ClientBase::uninstallWatch( unsigned _i ) { return true; } -LocalisedLogEntries ClientBase::peekWatch( unsigned _watchId ) const { - Guard l( x_filtersWatches ); - - // LOG(m_loggerWatch) << "peekWatch" << _watchId; - auto& w = m_watches.at( _watchId ); - // LOG(m_loggerWatch) << "lastPoll updated to " << - // chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); - if ( w.lastPoll != chrono::system_clock::time_point::max() ) - w.lastPoll = chrono::system_clock::now(); - return w.get_changes(); -} - LocalisedLogEntries ClientBase::checkWatch( unsigned _watchId ) { Guard l( x_filtersWatches ); LocalisedLogEntries ret; diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index 1dd089b72..626387b5a 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -114,7 +114,6 @@ class ClientBase : public Interface { fnClientWatchHandlerMulti_t fnOnNewChanges = fnClientWatchHandlerMulti_t(), bool isWS = false ) override; bool uninstallWatch( unsigned _watchId ) override; - LocalisedLogEntries peekWatch( unsigned _watchId ) const override; LocalisedLogEntries checkWatch( unsigned _watchId ) override; using Interface::blockDetails; diff --git a/libethereum/Interface.h b/libethereum/Interface.h index a6c1c874f..7ee5d0e26 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -154,13 +154,6 @@ class Interface { fnClientWatchHandlerMulti_t fnOnNewChanges = fnClientWatchHandlerMulti_t(), bool isWS = false ) = 0; virtual bool uninstallWatch( unsigned _watchId ) = 0; - LocalisedLogEntries peekWatchSafe( unsigned _watchId ) const { - try { - return peekWatch( _watchId ); - } catch ( ... ) { - return LocalisedLogEntries(); - } - } LocalisedLogEntries checkWatchSafe( unsigned _watchId ) { try { return checkWatch( _watchId ); @@ -168,7 +161,6 @@ class Interface { return LocalisedLogEntries(); } } - virtual LocalisedLogEntries peekWatch( unsigned _watchId ) const = 0; virtual LocalisedLogEntries checkWatch( unsigned _watchId ) = 0; // [BLOCK QUERY API] @@ -328,7 +320,6 @@ class Watch : public boost::noncopyable { } LocalisedLogEntries check() { return m_c ? m_c->checkWatch( m_id ) : LocalisedLogEntries(); } - LocalisedLogEntries peek() { return m_c ? m_c->peekWatch( m_id ) : LocalisedLogEntries(); } LocalisedLogEntries logs() const { return m_c->logs( m_id ); } private: diff --git a/libskutils/include/skutils/console_colors.h b/libskutils/include/skutils/console_colors.h index 36808e1ed..fdce082ff 100644 --- a/libskutils/include/skutils/console_colors.h +++ b/libskutils/include/skutils/console_colors.h @@ -438,12 +438,13 @@ extern bool string2duration( const std::string& s, std::chrono::duration< uint64 const std::chrono::seconds& seconds = std::chrono::seconds::zero() ); extern std::string duration2string( std::chrono::nanoseconds time ); extern std::string time2string( - std::time_t tt, uint64_t nMicroSeconds, bool isUTC = false, bool isColored = true ); -extern std::string time2string( const std::tm& aTm, uint64_t nMicroSeconds, bool isColored = true ); + std::time_t tt, uint64_t nMicroSeconds, bool isUTC = false, bool isColored = false ); +extern std::string time2string( + const std::tm& aTm, uint64_t nMicroSeconds, bool isColored = false ); extern std::string time2string( const default_clock_t::time_point& ptTime, bool isUTC = false, - bool isDaysInsteadOfYMD = false, bool isColored = true ); + bool isDaysInsteadOfYMD = false, bool isColored = false ); extern std::string now2string( - bool isUTC = false, bool isDaysInsteadOfYMD = false, bool isColored = true ); + bool isUTC = false, bool isDaysInsteadOfYMD = false, bool isColored = false ); extern std::string jsNow2string( bool isUTC = true ); inline std::string c() { diff --git a/libskutils/src/console_colors.cpp b/libskutils/src/console_colors.cpp index 042d4be3e..27969977d 100644 --- a/libskutils/src/console_colors.cpp +++ b/libskutils/src/console_colors.cpp @@ -1185,12 +1185,12 @@ std::string duration2string( std::chrono::nanoseconds time ) { } std::string time2string( - std::time_t tt, uint64_t nMicroSeconds, bool isUTC, bool isColored /*= true*/ ) { + std::time_t tt, uint64_t nMicroSeconds, bool isUTC, bool isColored /*= false*/ ) { std::lock_guard< std::mutex > lock( g_libcall_mutex ); struct std::tm aTm = isUTC ? ( *std::gmtime( &tt ) ) : ( *std::localtime( &tt ) ); return time2string( aTm, nMicroSeconds, isColored ); } -std::string time2string( const std::tm& aTm, uint64_t nMicroSeconds, bool isColored /*= true*/ ) { +std::string time2string( const std::tm& aTm, uint64_t nMicroSeconds, bool isColored /*= false*/ ) { const std::tm& effective_tm = aTm; std::stringstream ss; ss << std::setfill( '0' ); @@ -1255,7 +1255,7 @@ inline time_t clock_2_time_t( const typename clock_type_t::time_point& ptTime ) } std::string time2string( const default_clock_t::time_point& ptTime, bool isUTC, - bool isDaysInsteadOfYMD, bool isColored /*= true*/ ) { + bool isDaysInsteadOfYMD, bool isColored /*= false*/ ) { std::stringstream ss; typedef std::chrono::duration< int, std::ratio_multiply< std::chrono::hours::period, std::ratio< 24 > >::type > @@ -1363,7 +1363,7 @@ std::string time2string( const default_clock_t::time_point& ptTime, bool isUTC, std::string s = ss.str(); return s; } -std::string now2string( bool isUTC, bool isDaysInsteadOfYMD, bool isColored /*= true*/ ) { +std::string now2string( bool isUTC, bool isDaysInsteadOfYMD, bool isColored /*= false*/ ) { default_clock_t::time_point ptTimeNow = default_clock_t::now(); std::string s = time2string( ptTimeNow, isUTC, isDaysInsteadOfYMD, isColored ); return s; diff --git a/libweb3jsonrpc/Eth.cpp b/libweb3jsonrpc/Eth.cpp index b50874935..d45b3a541 100644 --- a/libweb3jsonrpc/Eth.cpp +++ b/libweb3jsonrpc/Eth.cpp @@ -487,11 +487,9 @@ string Eth::eth_call( TransactionSkeleton& t, string const& if ( strRevertReason.empty() ) strRevertReason = "EVM revert instruction without description message"; std::string strTx = t.toString(); - std::string strOut = cc::fatal( "Error message from eth_call():" ) + cc::error( " " ) + - cc::warn( strRevertReason ) + cc::error( ", with call arguments: " ) + - cc::j( strTx ) + cc::error( ", and using " ) + - cc::info( "blockNumber" ) + cc::error( "=" ) + - cc::bright( blockNumber ); + std::string strOut = "Error message from eth_call(): " + strRevertReason + + ", with call arguments: " + strTx + + ", and using blockNumber=" + blockNumber; cerror << strOut; throw std::logic_error( strRevertReason ); } @@ -820,6 +818,10 @@ Json::Value Eth::eth_getFilterChangesEx( string const& _filterId ) { Json::Value Eth::eth_getFilterLogs( string const& _filterId ) { try { return toJson( client()->logs( static_cast< unsigned int >( jsToInt( _filterId ) ) ) ); + } catch ( const TooBigResponse& ) { + BOOST_THROW_EXCEPTION( JsonRpcException( Errors::ERROR_RPC_INVALID_PARAMS, + "Log response size exceeded. Maximum allowed number of requested blocks is " + + to_string( this->client()->chainParams().getLogsBlocksLimit ) ) ); } catch ( ... ) { BOOST_THROW_EXCEPTION( JsonRpcException( Errors::ERROR_RPC_INVALID_PARAMS ) ); } @@ -837,6 +839,10 @@ Json::Value Eth::eth_getFilterLogs( string const& _filterId ) { Json::Value Eth::eth_getLogs( Json::Value const& _json ) { try { return toJson( client()->logs( toLogFilter( _json ) ) ); + } catch ( const TooBigResponse& ) { + BOOST_THROW_EXCEPTION( JsonRpcException( Errors::ERROR_RPC_INVALID_PARAMS, + "Log response size exceeded. Maximum allowed number of requested blocks is " + + to_string( this->client()->chainParams().getLogsBlocksLimit ) ) ); } catch ( ... ) { BOOST_THROW_EXCEPTION( JsonRpcException( Errors::ERROR_RPC_INVALID_PARAMS ) ); } diff --git a/test/unittests/libweb3jsonrpc/jsonrpc.cpp b/test/unittests/libweb3jsonrpc/jsonrpc.cpp index 6e5d772f6..229136584 100644 --- a/test/unittests/libweb3jsonrpc/jsonrpc.cpp +++ b/test/unittests/libweb3jsonrpc/jsonrpc.cpp @@ -254,7 +254,8 @@ ChainParams chainParams; JsonRpcFixture( const std::string& _config = "", bool _owner = true, bool _deploymentControl = true, bool _generation2 = false, - bool _mtmEnabled = false, bool _isSyncNode = false, int _emptyBlockIntervalMs = -1 ) { + bool _mtmEnabled = false, bool _isSyncNode = false, int _emptyBlockIntervalMs = -1, + const std::map& params = std::map() ) { if ( _config != "" ) { @@ -307,6 +308,9 @@ JsonRpcFixture( const std::string& _config = "", bool _owner = true, chainParams.nodeInfo.port = chainParams.nodeInfo.port6 = rand_port; chainParams.sChain.nodes[0].port = chainParams.sChain.nodes[0].port6 = rand_port; chainParams.skaleDisableChainIdCheck = true; + + if( params.count("getLogsBlocksLimit") && stoi( params.at( "getLogsBlocksLimit" ) ) ) + chainParams.getLogsBlocksLimit = stoi( params.at( "getLogsBlocksLimit" ) ); } chainParams.sChain.multiTransactionMode = _mtmEnabled; chainParams.nodeInfo.syncNode = _isSyncNode; @@ -2110,6 +2114,77 @@ contract Logger{ BOOST_REQUIRE_EQUAL(logs.size(), 24); } +// limit on getLogs output +BOOST_AUTO_TEST_CASE( getLogs_limit ) { + JsonRpcFixture fixture( "", true, true, false, false, false, -1, + {{"getLogsBlocksLimit", "10"}} ); + + dev::eth::simulateMining( *( fixture.client ), 1 ); + + /* + // SPDX-License-Identifier: None +pragma solidity ^0.8; +contract Logger{ + event DummyEvent(uint256, uint256); + fallback() external payable { + for(uint i=0; i<100; ++i) + emit DummyEvent(block.number, i); + } +} +*/ + + string bytecode = "6080604052348015600e575f80fd5b5060c080601a5f395ff3fe60806040525f5b6064811015604f577f90778767414a5c844b9d35a8745f67697ee3b8c2c3f4feafe5d9a3e234a5a3654382604051603d9291906067565b60405180910390a18060010190506006565b005b5f819050919050565b6061816051565b82525050565b5f60408201905060785f830185605a565b60836020830184605a565b939250505056fea264697066735822122040208e35f2706dd92c17579466ab671c308efec51f558a755ea2cf81105ab22964736f6c63430008190033"; + + Json::Value create; + create["code"] = bytecode; + create["gas"] = "180000"; // TODO or change global default of 90000? + + string deployHash = fixture.rpcClient->eth_sendTransaction( create ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + + Json::Value deployReceipt = fixture.rpcClient->eth_getTransactionReceipt( deployHash ); + string contractAddress = deployReceipt["contractAddress"].asString(); + + // generate 10 blocks 10 logs each + + Json::Value t; + t["from"] = toJS( fixture.coinbase.address() ); + t["value"] = jsToDecimal( "0" ); + t["to"] = contractAddress; + t["gas"] = "99000"; + + for(int i=0; i<11; ++i){ + + std::string txHash = fixture.rpcClient->eth_sendTransaction( t ); + BOOST_REQUIRE( !txHash.empty() ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + Json::Value receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE_EQUAL(receipt["status"], "0x1"); + } + + // ask for logs + Json::Value req; + req["fromBlock"] = 1; + req["toBlock"] = 11; + req["topics"] = Json::Value(Json::arrayValue); + + // 1 10 blocks + BOOST_REQUIRE_NO_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req) ); + + // 2 with topics + req["address"] = contractAddress; + BOOST_REQUIRE_NO_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req) ); + + // 3 11 blocks + req["toBlock"] = 12; + BOOST_REQUIRE_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req), std::exception ); + + // 4 filter + string filterId = fixture.rpcClient->eth_newFilter( req ); + BOOST_REQUIRE_THROW( Json::Value res = fixture.rpcClient->eth_getFilterLogs(filterId), std::exception ); + BOOST_REQUIRE_NO_THROW( Json::Value res = fixture.rpcClient->eth_getFilterChanges(filterId) ); +} + BOOST_AUTO_TEST_CASE( estimate_gas_low_gas_txn ) { JsonRpcFixture fixture; dev::eth::simulateMining( *( fixture.client ), 10 );