From 907a5c4559355fb83965e13c645879e60d05a3f3 Mon Sep 17 00:00:00 2001 From: Dima Litvinov Date: Tue, 2 Apr 2024 19:27:11 +0100 Subject: [PATCH 1/8] IS-833 Limit on storage for selfdestruct and test --- libethcore/ChainOperationParams.h | 1 + libethereum/ChainParams.cpp | 3 + libethereum/ExtVM.cpp | 6 ++ libevm/VMFace.h | 1 + test/unittests/libweb3jsonrpc/jsonrpc.cpp | 95 +++++++++++++++++++++++ 5 files changed, 106 insertions(+) diff --git a/libethcore/ChainOperationParams.h b/libethcore/ChainOperationParams.h index 5d773e216..65078a38d 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 + int64_t maxStorageForSelfdestruct = -1; time_t getPatchTimestamp( SchainPatchEnum _patchEnum ) const; diff --git a/libethereum/ChainParams.cpp b/libethereum/ChainParams.cpp index ca4f51b6b..aee6b854c 100644 --- a/libethereum/ChainParams.cpp +++ b/libethereum/ChainParams.cpp @@ -106,6 +106,9 @@ ChainParams ChainParams::loadConfig( cp.skaleDisableChainIdCheck = params.count( c_skaleDisableChainIdCheck ) ? params[c_skaleDisableChainIdCheck].get_bool() : false; + cp.maxStorageForSelfdestruct = params.count( "maxStorageForSelfdestruct" ) ? + params.at( "maxStorageForSelfdestruct" ).get_int64() : + -1; if ( obj.count( c_skaleConfig ) ) { processSkaleConfigItems( cp, obj ); diff --git a/libethereum/ExtVM.cpp b/libethereum/ExtVM.cpp index 453cb0ec0..885e1b19e 100644 --- a/libethereum/ExtVM.cpp +++ b/libethereum/ExtVM.cpp @@ -170,6 +170,12 @@ CreateResult ExtVM::create( u256 _endowment, u256& io_gas, bytesConstRef _code, } void ExtVM::suicide( Address _a ) { + if ( m_sealEngine.chainParams().maxStorageForSelfdestruct >= 0 && + m_s.storageUsed( this->myAddress ) > + m_sealEngine.chainParams().maxStorageForSelfdestruct ) { + BOOST_THROW_EXCEPTION( TooBigForSelfdestruct() ); + } + // Why transfer is not used here? That caused a consensus issue before (see Quirk #2 in // http://martin.swende.se/blog/Ethereum_quirks_and_vulns.html). There is one test case // witnessing the current consensus diff --git a/libevm/VMFace.h b/libevm/VMFace.h index d463a2883..5952bfcea 100644 --- a/libevm/VMFace.h +++ b/libevm/VMFace.h @@ -38,6 +38,7 @@ ETH_SIMPLE_EXCEPTION_VM( DisallowedStateChange ); ETH_SIMPLE_EXCEPTION_VM( BufferOverrun ); ETH_SIMPLE_EXCEPTION_VM( StorageOverflow ); ETH_SIMPLE_EXCEPTION_VM( InvalidContractDeployer ); +ETH_SIMPLE_EXCEPTION_VM( TooBigForSelfdestruct ); /// Reports VM internal error. This is not based on VMException because it must be handled /// differently than defined consensus exceptions. diff --git a/test/unittests/libweb3jsonrpc/jsonrpc.cpp b/test/unittests/libweb3jsonrpc/jsonrpc.cpp index 6e5d772f6..88b799b9c 100644 --- a/test/unittests/libweb3jsonrpc/jsonrpc.cpp +++ b/test/unittests/libweb3jsonrpc/jsonrpc.cpp @@ -300,6 +300,7 @@ JsonRpcFixture( const std::string& _config = "", bool _owner = true, push0PatchActivationTimestamp = time(nullptr) + 10; chainParams.sChain._patchTimestamps[static_cast(SchainPatchEnum::PushZeroPatch)] = push0PatchActivationTimestamp; chainParams.sChain.emptyBlockIntervalMs = _emptyBlockIntervalMs; + chainParams.maxStorageForSelfdestruct = 64; // add random extra data to randomize genesis hash and get random DB path, // so that tests can be run in parallel // TODO: better make it use ethemeral in-memory databases @@ -3636,4 +3637,98 @@ BOOST_AUTO_TEST_CASE( test_exceptions ) { BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_CASE( suicide_storage_limit ) { + JsonRpcFixture fixture; + dev::eth::simulateMining( *( fixture.client ), 10 ); + + // pragma solidity ^0.8.0; + + // contract SuicideLimit { + + // uint[] public storageArray; + + // function store(uint32 size) public { + // for(uint32 i=0; ieth_sendTransaction( create ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + + Json::Value receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE_EQUAL( receipt["status"], string( "0x1" ) ); + string contractAddress = receipt["contractAddress"].asString(); + dev::Address contract = dev::Address( contractAddress ); + + // 1 fill data + Json::Value txStore; + txStore["to"] = contractAddress; + txStore["data"] = "0xb9e953820000000000000000000000000000000000000000000000000000000000000002"; + txStore["from"] = toJS( senderAddress ); + txStore["gas"] = "500000"; + txStore["gasPrice"] = fixture.rpcClient->eth_gasPrice(); + txHash = fixture.rpcClient->eth_sendTransaction( txStore ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + Json::Value storeReceipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE_EQUAL( storeReceipt["status"], string( "0x1" ) ); + BOOST_REQUIRE_EQUAL( fixture.client->state().storageUsed( contract ), 96 ); + + // 2 suicide should fail + Json::Value txSuicide; + txSuicide["to"] = contractAddress; + txSuicide["data"] = "0x2b68b9c6"; + txSuicide["from"] = toJS( senderAddress ); + txSuicide["gas"] = "500000"; + txSuicide["gasPrice"] = fixture.rpcClient->eth_gasPrice(); + txHash = fixture.rpcClient->eth_sendTransaction( txSuicide ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + Json::Value suicideReceipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE_EQUAL( suicideReceipt["status"], string( "0x0" ) ); + + // 3 free 1 + Json::Value txFree; + txFree["to"] = contractAddress; + txFree["data"] = "0x93edc68f0000000000000000000000000000000000000000000000000000000000000001"; + txFree["from"] = toJS( senderAddress ); + txFree["gas"] = "500000"; + txFree["gasPrice"] = fixture.rpcClient->eth_gasPrice(); + txHash = fixture.rpcClient->eth_sendTransaction( txFree ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + Json::Value freeReceipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE_EQUAL( freeReceipt["status"], string( "0x1" ) ); + BOOST_REQUIRE_EQUAL( fixture.client->state().storageUsed( contract ), 64 ); + + // 4 suicide should succeed + txHash = fixture.rpcClient->eth_sendTransaction( txSuicide ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + suicideReceipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE_EQUAL( suicideReceipt["status"], string( "0x1" ) ); +} + BOOST_AUTO_TEST_SUITE_END() From 8f72bbd7efb63939039434fd6e3d1fcc36e0d9e7 Mon Sep 17 00:00:00 2001 From: Dima Litvinov Date: Fri, 5 Apr 2024 13:55:40 +0100 Subject: [PATCH 2/8] IS-833 Test for patch timestamp --- test/unittests/libweb3jsonrpc/jsonrpc.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/test/unittests/libweb3jsonrpc/jsonrpc.cpp b/test/unittests/libweb3jsonrpc/jsonrpc.cpp index 88b799b9c..7761f1113 100644 --- a/test/unittests/libweb3jsonrpc/jsonrpc.cpp +++ b/test/unittests/libweb3jsonrpc/jsonrpc.cpp @@ -47,6 +47,8 @@ #include #include #include +#include +#include #include #include @@ -252,10 +254,10 @@ struct JsonRpcFixture : public TestOutputHelperFixture { 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 ) { - + JsonRpcFixture( const std::string& _config = "", bool _owner = true, + bool _deploymentControl = true, bool _generation2 = false, + bool _mtmEnabled = false, bool _isSyncNode = false, int _emptyBlockIntervalMs = -1, + const std::map& params = std::map() ) { if ( _config != "" ) { if ( !_generation2 ) { @@ -312,6 +314,9 @@ JsonRpcFixture( const std::string& _config = "", bool _owner = true, chainParams.sChain.multiTransactionMode = _mtmEnabled; chainParams.nodeInfo.syncNode = _isSyncNode; + if( params.count("selfdestructStorageLimitPatchTimestamp") && stoi( params.at( "selfdestructStorageLimitPatchTimestamp" ) ) ) + chainParams.sChain._patchTimestamps[static_cast(SchainPatchEnum::SelfdestructStorageLimitPatch)] = stoi( params.at( "selfdestructStorageLimitPatchTimestamp" ) ); + // web3.reset( new WebThreeDirect( // "eth tests", tempDir.path(), "", chainParams, WithExisting::Kill, {"eth"}, // true ) ); @@ -795,7 +800,7 @@ BOOST_AUTO_TEST_CASE( send_raw_tx_sync ) { // Sending tx to sync node string txHash = fixture.rpcClient->eth_sendTransaction( create ); - + auto pendingTransactions = fixture.client->pending(); BOOST_REQUIRE( pendingTransactions.size() == 1); auto txHashFromQueue = "0x" + pendingTransactions[0].sha3().hex(); @@ -1727,7 +1732,7 @@ BOOST_AUTO_TEST_CASE( call_from_parameter ) { "fffffffffffffffffffffffff16815260200191505060405180910390f3" "5b60003390509056fea165627a7a72305820abfa953fead48d8f657bca6" "57713501650734d40342585cafcf156a3fe1f41d20029"; - + auto senderAddress = fixture.coinbase.address(); Json::Value create; @@ -3507,7 +3512,7 @@ BOOST_AUTO_TEST_CASE( cached_filestorage ) { auto _config = c_genesisConfigString; Json::Value ret; Json::Reader().parse( _config, ret ); - ret["skaleConfig"]["sChain"]["revertableFSPatchTimestamp"] = 1; + ret["skaleConfig"]["sChain"]["revertableFSPatchTimestamp"] = 1; Json::FastWriter fastWriter; std::string config = fastWriter.write( ret ); RestrictedAddressFixture fixture( config ); From 574cb799f479ec49cc3c06bd82fb732120732891 Mon Sep 17 00:00:00 2001 From: Dima Litvinov Date: Thu, 11 Apr 2024 18:42:53 +0100 Subject: [PATCH 3/8] IS-833 Formatting --- libethereum/ExtVM.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libethereum/ExtVM.cpp b/libethereum/ExtVM.cpp index 885e1b19e..5bf1155c8 100644 --- a/libethereum/ExtVM.cpp +++ b/libethereum/ExtVM.cpp @@ -170,9 +170,8 @@ CreateResult ExtVM::create( u256 _endowment, u256& io_gas, bytesConstRef _code, } void ExtVM::suicide( Address _a ) { - if ( m_sealEngine.chainParams().maxStorageForSelfdestruct >= 0 && - m_s.storageUsed( this->myAddress ) > - m_sealEngine.chainParams().maxStorageForSelfdestruct ) { + if ( m_chainParams.maxStorageForSelfdestruct >= 0 && + m_s.storageUsed( this->myAddress ) > m_chainParams.maxStorageForSelfdestruct ) { BOOST_THROW_EXCEPTION( TooBigForSelfdestruct() ); } From 94a820ad1c0f00a642fd66ce66493ca875a3820d Mon Sep 17 00:00:00 2001 From: Dima Litvinov Date: Thu, 18 Apr 2024 18:49:19 +0100 Subject: [PATCH 4/8] IS-833 Tests --- libethcore/Exceptions.h | 2 ++ libethereum/ClientBase.cpp | 10 ++++++++-- libethereum/ExtVM.cpp | 6 ++++-- test/unittests/libweb3jsonrpc/jsonrpc.cpp | 24 +++++++++++++++++------ 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h index 60a76954c..bb96b44bf 100644 --- a/libethcore/Exceptions.h +++ b/libethcore/Exceptions.h @@ -107,5 +107,7 @@ 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/ClientBase.cpp b/libethereum/ClientBase.cpp index bc88f0e34..4a0cab44e 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -239,13 +239,19 @@ LocalisedLogEntries ClientBase::logs( LogFilter const& _f ) const { std::vector< unsigned > matchingBlocksVector = bc().withBlockBloom( i, end, begin ); matchingBlocks.insert( matchingBlocksVector.begin(), matchingBlocksVector.end() ); } - else + else { // if it is a range filter, we want to get all logs from all blocks in given range + if ( end >= begin && end - begin >= 2000 ) + BOOST_THROW_EXCEPTION( TooBigResponse() ); for ( unsigned i = end; i <= begin; i++ ) matchingBlocks.insert( i ); + } - for ( auto n : matchingBlocks ) + for ( auto n : matchingBlocks ) { prependLogsFromBlock( _f, bc().numberHash( n ), BlockPolarity::Live, ret ); + if ( ret.size() > 10000 && !_f.isRangeFilter() ) + BOOST_THROW_EXCEPTION( TooBigResponse() ); + } reverse( ret.begin(), ret.end() ); return ret; diff --git a/libethereum/ExtVM.cpp b/libethereum/ExtVM.cpp index 5bf1155c8..50d04989d 100644 --- a/libethereum/ExtVM.cpp +++ b/libethereum/ExtVM.cpp @@ -23,7 +23,7 @@ #include "ExtVM.h" -#include +#include #include @@ -170,7 +170,9 @@ CreateResult ExtVM::create( u256 _endowment, u256& io_gas, bytesConstRef _code, } void ExtVM::suicide( Address _a ) { - if ( m_chainParams.maxStorageForSelfdestruct >= 0 && + if ( SelfdestructStorageLimitPatch::isEnabledWhen( + this->envInfo().committedBlockTimestamp() ) && + m_chainParams.maxStorageForSelfdestruct >= 0 && m_s.storageUsed( this->myAddress ) > m_chainParams.maxStorageForSelfdestruct ) { BOOST_THROW_EXCEPTION( TooBigForSelfdestruct() ); } diff --git a/test/unittests/libweb3jsonrpc/jsonrpc.cpp b/test/unittests/libweb3jsonrpc/jsonrpc.cpp index 7761f1113..ff7bbc6be 100644 --- a/test/unittests/libweb3jsonrpc/jsonrpc.cpp +++ b/test/unittests/libweb3jsonrpc/jsonrpc.cpp @@ -3642,9 +3642,11 @@ BOOST_AUTO_TEST_CASE( test_exceptions ) { BOOST_AUTO_TEST_SUITE_END() +auto selfdestructVariants = boost::unit_test::data::make({string("0"), string("1")}); -BOOST_AUTO_TEST_CASE( suicide_storage_limit ) { - JsonRpcFixture fixture; +BOOST_DATA_TEST_CASE( suicide_storage_limit, selfdestructVariants, selfdestructPatch ) { + JsonRpcFixture fixture( "", true, true, false, false, false, -1, + {{"selfdestructStorageLimitPatchTimestamp", selfdestructPatch}} ); dev::eth::simulateMining( *( fixture.client ), 10 ); // pragma solidity ^0.8.0; @@ -3714,7 +3716,10 @@ BOOST_AUTO_TEST_CASE( suicide_storage_limit ) { txHash = fixture.rpcClient->eth_sendTransaction( txSuicide ); dev::eth::mineTransaction( *( fixture.client ), 1 ); Json::Value suicideReceipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); - BOOST_REQUIRE_EQUAL( suicideReceipt["status"], string( "0x0" ) ); + if(selfdestructPatch == "1") + BOOST_REQUIRE_EQUAL( suicideReceipt["status"], string( "0x0" ) ); + else + BOOST_REQUIRE_EQUAL( suicideReceipt["status"], string( "0x1" ) ); // 3 free 1 Json::Value txFree; @@ -3726,10 +3731,17 @@ BOOST_AUTO_TEST_CASE( suicide_storage_limit ) { txHash = fixture.rpcClient->eth_sendTransaction( txFree ); dev::eth::mineTransaction( *( fixture.client ), 1 ); Json::Value freeReceipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); - BOOST_REQUIRE_EQUAL( freeReceipt["status"], string( "0x1" ) ); - BOOST_REQUIRE_EQUAL( fixture.client->state().storageUsed( contract ), 64 ); - // 4 suicide should succeed + if(selfdestructPatch == "1"){ + BOOST_REQUIRE_EQUAL( freeReceipt["status"], string( "0x1" ) ); + BOOST_REQUIRE_EQUAL( fixture.client->state().storageUsed( contract ), 64 ); + } + else{ + BOOST_REQUIRE_EQUAL( freeReceipt["status"], string( "0x1" ) ); + BOOST_REQUIRE_EQUAL( fixture.client->state().storageUsed( contract ), 0 ); + } + + // 4 suicide should succeed or ignored txHash = fixture.rpcClient->eth_sendTransaction( txSuicide ); dev::eth::mineTransaction( *( fixture.client ), 1 ); suicideReceipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); From b63c4590a4a23bbb9c77c8fb4504e0f90957f396 Mon Sep 17 00:00:00 2001 From: Dima Litvinov Date: Mon, 22 Apr 2024 23:00:11 +0100 Subject: [PATCH 5/8] IS-833 Tests --- libethcore/ChainOperationParams.h | 2 + libethereum/ChainParams.cpp | 4 ++ libethereum/ClientBase.cpp | 5 +- test/unittests/libweb3jsonrpc/jsonrpc.cpp | 74 +++++++++++++++++++++++ 4 files changed, 83 insertions(+), 2 deletions(-) diff --git a/libethcore/ChainOperationParams.h b/libethcore/ChainOperationParams.h index 65078a38d..c346b6e3d 100644 --- a/libethcore/ChainOperationParams.h +++ b/libethcore/ChainOperationParams.h @@ -257,6 +257,8 @@ struct ChainOperationParams { typedef std::vector< std::string > vecAdminOrigins_t; vecAdminOrigins_t vecAdminOrigins; // wildcard based folters for IP addresses int64_t maxStorageForSelfdestruct = -1; + int getLogsBlocksLimit = -1; + int getLogsRecordsLimit = -1; time_t getPatchTimestamp( SchainPatchEnum _patchEnum ) const; diff --git a/libethereum/ChainParams.cpp b/libethereum/ChainParams.cpp index aee6b854c..531fd510a 100644 --- a/libethereum/ChainParams.cpp +++ b/libethereum/ChainParams.cpp @@ -109,6 +109,10 @@ ChainParams ChainParams::loadConfig( cp.maxStorageForSelfdestruct = params.count( "maxStorageForSelfdestruct" ) ? params.at( "maxStorageForSelfdestruct" ).get_int64() : -1; + cp.getLogsBlocksLimit = + params.count( "getLogsBlocksLimit" ) ? params.at( "getLogsBlocksLimit" ).get_int() : -1; + cp.getLogsRecordsLimit = + params.count( "getLogsRecordsLimit" ) ? params.at( "getLogsRecordsLimit" ).get_int() : -1; if ( obj.count( c_skaleConfig ) ) { processSkaleConfigItems( cp, obj ); diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 4a0cab44e..45a89cdd0 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -241,15 +241,16 @@ LocalisedLogEntries ClientBase::logs( LogFilter const& _f ) const { } else { // if it is a range filter, we want to get all logs from all blocks in given range - if ( end >= begin && end - begin >= 2000 ) + if ( begin >= end && begin - end >= bc().chainParams().getLogsBlocksLimit ) BOOST_THROW_EXCEPTION( TooBigResponse() ); for ( unsigned i = end; i <= begin; i++ ) matchingBlocks.insert( i ); } + int recordsLimit = bc().chainParams().getLogsRecordsLimit; for ( auto n : matchingBlocks ) { prependLogsFromBlock( _f, bc().numberHash( n ), BlockPolarity::Live, ret ); - if ( ret.size() > 10000 && !_f.isRangeFilter() ) + if ( ret.size() > recordsLimit && !_f.isRangeFilter() ) BOOST_THROW_EXCEPTION( TooBigResponse() ); } diff --git a/test/unittests/libweb3jsonrpc/jsonrpc.cpp b/test/unittests/libweb3jsonrpc/jsonrpc.cpp index ff7bbc6be..979fcd671 100644 --- a/test/unittests/libweb3jsonrpc/jsonrpc.cpp +++ b/test/unittests/libweb3jsonrpc/jsonrpc.cpp @@ -316,6 +316,10 @@ ChainParams chainParams; if( params.count("selfdestructStorageLimitPatchTimestamp") && stoi( params.at( "selfdestructStorageLimitPatchTimestamp" ) ) ) chainParams.sChain._patchTimestamps[static_cast(SchainPatchEnum::SelfdestructStorageLimitPatch)] = stoi( params.at( "selfdestructStorageLimitPatchTimestamp" ) ); + if( params.count("getLogsBlocksLimit") && stoi( params.at( "getLogsBlocksLimit" ) ) ) + chainParams.getLogsBlocksLimit = stoi( params.at( "getLogsBlocksLimit" ) ); + if( params.count("getLogsRecordsLimit") && stoi( params.at( "getLogsRecordsLimit" ) ) ) + chainParams.getLogsRecordsLimit = stoi( params.at( "getLogsRecordsLimit" ) ); // web3.reset( new WebThreeDirect( // "eth tests", tempDir.path(), "", chainParams, WithExisting::Kill, {"eth"}, @@ -2116,6 +2120,76 @@ 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"}, + {"getLogsRecordsLimit", "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<10; ++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"] = 10; + req["topics"] = Json::Value(Json::arrayValue); + + // 1 10 blocks + BOOST_REQUIRE_NO_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req) ); + + // 2 11 blocks + req["toBlock"] = 11; + BOOST_REQUIRE_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req), std::exception ); + + // with topics + req["address"] = contractAddress; + BOOST_REQUIRE_NO_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req) ); +} + BOOST_AUTO_TEST_CASE( estimate_gas_low_gas_txn ) { JsonRpcFixture fixture; dev::eth::simulateMining( *( fixture.client ), 10 ); From 05ad9df6a53cc0b091c35a6520bfea60957d6061 Mon Sep 17 00:00:00 2001 From: Dima Litvinov Date: Wed, 24 Apr 2024 16:49:05 +0100 Subject: [PATCH 6/8] IS-833 Limit block count --- libethcore/ChainOperationParams.h | 1 - libethereum/ChainParams.cpp | 2 -- libethereum/ClientBase.cpp | 8 +++----- libweb3jsonrpc/Eth.cpp | 8 ++++++++ test/unittests/libweb3jsonrpc/jsonrpc.cpp | 15 ++++++--------- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/libethcore/ChainOperationParams.h b/libethcore/ChainOperationParams.h index c346b6e3d..07ba710f9 100644 --- a/libethcore/ChainOperationParams.h +++ b/libethcore/ChainOperationParams.h @@ -258,7 +258,6 @@ struct ChainOperationParams { vecAdminOrigins_t vecAdminOrigins; // wildcard based folters for IP addresses int64_t maxStorageForSelfdestruct = -1; int getLogsBlocksLimit = -1; - int getLogsRecordsLimit = -1; time_t getPatchTimestamp( SchainPatchEnum _patchEnum ) const; diff --git a/libethereum/ChainParams.cpp b/libethereum/ChainParams.cpp index 531fd510a..cc7a37354 100644 --- a/libethereum/ChainParams.cpp +++ b/libethereum/ChainParams.cpp @@ -111,8 +111,6 @@ ChainParams ChainParams::loadConfig( -1; cp.getLogsBlocksLimit = params.count( "getLogsBlocksLimit" ) ? params.at( "getLogsBlocksLimit" ).get_int() : -1; - cp.getLogsRecordsLimit = - params.count( "getLogsRecordsLimit" ) ? params.at( "getLogsRecordsLimit" ).get_int() : -1; if ( obj.count( c_skaleConfig ) ) { processSkaleConfigItems( cp, obj ); diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 45a89cdd0..cdb5609a0 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(); @@ -241,17 +244,12 @@ LocalisedLogEntries ClientBase::logs( LogFilter const& _f ) const { } else { // if it is a range filter, we want to get all logs from all blocks in given range - if ( begin >= end && begin - end >= bc().chainParams().getLogsBlocksLimit ) - BOOST_THROW_EXCEPTION( TooBigResponse() ); for ( unsigned i = end; i <= begin; i++ ) matchingBlocks.insert( i ); } - int recordsLimit = bc().chainParams().getLogsRecordsLimit; for ( auto n : matchingBlocks ) { prependLogsFromBlock( _f, bc().numberHash( n ), BlockPolarity::Live, ret ); - if ( ret.size() > recordsLimit && !_f.isRangeFilter() ) - BOOST_THROW_EXCEPTION( TooBigResponse() ); } reverse( ret.begin(), ret.end() ); diff --git a/libweb3jsonrpc/Eth.cpp b/libweb3jsonrpc/Eth.cpp index b50874935..41c90ab18 100644 --- a/libweb3jsonrpc/Eth.cpp +++ b/libweb3jsonrpc/Eth.cpp @@ -820,6 +820,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 +841,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 979fcd671..281d57450 100644 --- a/test/unittests/libweb3jsonrpc/jsonrpc.cpp +++ b/test/unittests/libweb3jsonrpc/jsonrpc.cpp @@ -318,8 +318,6 @@ ChainParams chainParams; chainParams.sChain._patchTimestamps[static_cast(SchainPatchEnum::SelfdestructStorageLimitPatch)] = stoi( params.at( "selfdestructStorageLimitPatchTimestamp" ) ); if( params.count("getLogsBlocksLimit") && stoi( params.at( "getLogsBlocksLimit" ) ) ) chainParams.getLogsBlocksLimit = stoi( params.at( "getLogsBlocksLimit" ) ); - if( params.count("getLogsRecordsLimit") && stoi( params.at( "getLogsRecordsLimit" ) ) ) - chainParams.getLogsRecordsLimit = stoi( params.at( "getLogsRecordsLimit" ) ); // web3.reset( new WebThreeDirect( // "eth tests", tempDir.path(), "", chainParams, WithExisting::Kill, {"eth"}, @@ -2123,8 +2121,7 @@ contract Logger{ // limit on getLogs output BOOST_AUTO_TEST_CASE( getLogs_limit ) { JsonRpcFixture fixture( "", true, true, false, false, false, -1, - {{"getLogsBlocksLimit", "10"}, - {"getLogsRecordsLimit", "10"}} ); + {{"getLogsBlocksLimit", "10"}} ); dev::eth::simulateMining( *( fixture.client ), 1 ); @@ -2181,13 +2178,13 @@ contract Logger{ // 1 10 blocks BOOST_REQUIRE_NO_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req) ); - // 2 11 blocks - req["toBlock"] = 11; - BOOST_REQUIRE_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req), std::exception ); - - // with topics + // 2 with topics req["address"] = contractAddress; BOOST_REQUIRE_NO_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req) ); + + // 3 11 blocks + req["toBlock"] = 11; + BOOST_REQUIRE_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req), std::exception ); } BOOST_AUTO_TEST_CASE( estimate_gas_low_gas_txn ) { From 0734b8a710d2d8185de8729ca6ed520d5f41fae8 Mon Sep 17 00:00:00 2001 From: Dima Litvinov Date: Wed, 24 Apr 2024 17:54:27 +0100 Subject: [PATCH 7/8] IS-833 Limit watch and add tests --- libethereum/ClientBase.cpp | 12 ------------ libethereum/ClientBase.h | 1 - libethereum/Interface.h | 9 --------- test/unittests/libweb3jsonrpc/jsonrpc.cpp | 5 +++++ 4 files changed, 5 insertions(+), 22 deletions(-) diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index cdb5609a0..3f345ea5d 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -355,18 +355,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/test/unittests/libweb3jsonrpc/jsonrpc.cpp b/test/unittests/libweb3jsonrpc/jsonrpc.cpp index 281d57450..2f18d8d6b 100644 --- a/test/unittests/libweb3jsonrpc/jsonrpc.cpp +++ b/test/unittests/libweb3jsonrpc/jsonrpc.cpp @@ -2185,6 +2185,11 @@ contract Logger{ // 3 11 blocks req["toBlock"] = 11; 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 ) { From da1ce6243bf0cb3866c73139115e002e1991812f Mon Sep 17 00:00:00 2001 From: Dima Litvinov Date: Wed, 24 Apr 2024 18:24:46 +0100 Subject: [PATCH 8/8] IS-833 More easy check --- libethereum/ClientBase.cpp | 2 +- test/unittests/libweb3jsonrpc/jsonrpc.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 3f345ea5d..d4312b6d9 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -219,7 +219,7 @@ 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 ) + if ( begin >= end && begin - end > bc().chainParams().getLogsBlocksLimit ) BOOST_THROW_EXCEPTION( TooBigResponse() ); // Handle pending transactions differently as they're not on the block chain. diff --git a/test/unittests/libweb3jsonrpc/jsonrpc.cpp b/test/unittests/libweb3jsonrpc/jsonrpc.cpp index 2f18d8d6b..1160f7cc4 100644 --- a/test/unittests/libweb3jsonrpc/jsonrpc.cpp +++ b/test/unittests/libweb3jsonrpc/jsonrpc.cpp @@ -2160,7 +2160,7 @@ contract Logger{ t["to"] = contractAddress; t["gas"] = "99000"; - for(int i=0; i<10; ++i){ + for(int i=0; i<11; ++i){ std::string txHash = fixture.rpcClient->eth_sendTransaction( t ); BOOST_REQUIRE( !txHash.empty() ); @@ -2172,7 +2172,7 @@ contract Logger{ // ask for logs Json::Value req; req["fromBlock"] = 1; - req["toBlock"] = 10; + req["toBlock"] = 11; req["topics"] = Json::Value(Json::arrayValue); // 1 10 blocks @@ -2183,7 +2183,7 @@ contract Logger{ BOOST_REQUIRE_NO_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req) ); // 3 11 blocks - req["toBlock"] = 11; + req["toBlock"] = 12; BOOST_REQUIRE_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req), std::exception ); // 4 filter