diff --git a/libethcore/ChainOperationParams.h b/libethcore/ChainOperationParams.h index 3e442229d..678619900 100644 --- a/libethcore/ChainOperationParams.h +++ b/libethcore/ChainOperationParams.h @@ -178,6 +178,7 @@ struct SChain { time_t precompiledConfigPatchTimestamp = 0; time_t pushZeroPatchTimestamp = 0; time_t skipInvalidTransactionsPatchTimestamp = 0; + time_t correctForkInPowPatchTimestamp = 0; SChain() { name = "TestChain"; diff --git a/libethereum/Block.cpp b/libethereum/Block.cpp index 28e17ad0f..d98705d88 100644 --- a/libethereum/Block.cpp +++ b/libethereum/Block.cpp @@ -351,7 +351,7 @@ pair< TransactionReceipts, bool > Block::sync( // caller if we hit the limit for ( Transaction& transaction : transactions ) { - transaction.checkOutExternalGas( _bc.chainParams().externalGasDifficulty ); + transaction.checkOutExternalGas( _bc.chainParams(), _bc.number() ); } assert( _bc.currentHash() == m_currentBlock.parentHash() ); @@ -631,8 +631,7 @@ u256 Block::enact( VerifiedBlockRef const& _block, BlockChain const& _bc ) { // << " (state #" // << state().getNonce( tr.from() ) << ") value = " << tr.value() << // endl; - const_cast< Transaction& >( tr ).checkOutExternalGas( - _bc.chainParams().externalGasDifficulty ); + const_cast< Transaction& >( tr ).checkOutExternalGas( _bc.chainParams(), _bc.number() ); execute( _bc.lastBlockHashes(), tr ); // cerr << "Now: " // << "State #" << state().getNonce( tr.from() ) << endl; diff --git a/libethereum/ChainParams.cpp b/libethereum/ChainParams.cpp index fec3da5a5..52d3fade1 100644 --- a/libethereum/ChainParams.cpp +++ b/libethereum/ChainParams.cpp @@ -280,6 +280,11 @@ ChainParams ChainParams::loadConfig( sChainObj.at( "skipInvalidTransactionsPatchTimestamp" ).get_int64() : 0; + s.correctForkInPowPatchTimestamp = + sChainObj.count( "correctForkInPowPatchTimestamp" ) ? + sChainObj.at( "correctForkInPowPatchTimestamp" ).get_int64() : + 0; + if ( sChainObj.count( "nodeGroups" ) ) { std::vector< NodeGroup > nodeGroups; for ( const auto& nodeGroupConf : sChainObj["nodeGroups"].get_obj() ) { diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 8bf8af377..f2c34239f 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -53,6 +53,7 @@ #include #include +#include #include #include #include @@ -169,6 +170,7 @@ Client::Client( ChainParams const& _params, int _networkID, SkipInvalidTransactionsPatch::setTimestamp( this->chainParams().sChain.skipInvalidTransactionsPatchTimestamp ); PrecompiledConfigPatch::setTimestamp( chainParams().sChain.precompiledConfigPatchTimestamp ); + CorrectForkInPowPatch::setTimestamp( chainParams().sChain.correctForkInPowPatchTimestamp ); } @@ -331,6 +333,10 @@ void Client::init( WithExisting _forceAction, u256 _networkId ) { // HACK Needed to set env var for consensus AmsterdamFixPatch::isEnabled( *this ); + // needed for checkOutExternalGas + CorrectForkInPowPatch::lastBlockTimestamp = blockChain().info().timestamp(); + CorrectForkInPowPatch::lastBlockNumber = blockChain().number(); + initCPUUSage(); doWork( false ); @@ -600,6 +606,10 @@ size_t Client::importTransactionsAsBlock( sealUnconditionally( false ); importWorkingBlock(); + // this needs to be updated as soon as possible, as it's used in new transactions validation + CorrectForkInPowPatch::lastBlockTimestamp = blockChain().info().timestamp(); + CorrectForkInPowPatch::lastBlockNumber = blockChain().number(); + if ( !UnsafeRegion::isActive() ) { LOG( m_loggerDetail ) << "Total unsafe time so far = " << std::chrono::duration_cast< std::chrono::seconds >( @@ -668,6 +678,9 @@ size_t Client::syncTransactions( PushZeroPatch::lastBlockTimestamp = blockChain().info().timestamp(); SkipInvalidTransactionsPatch::lastBlockTimestamp = blockChain().info().timestamp(); PrecompiledConfigPatch::lastBlockTimestamp = blockChain().info().timestamp(); + CorrectForkInPowPatch::lastBlockTimestamp = blockChain().info().timestamp(); + CorrectForkInPowPatch::lastBlockNumber = blockChain().number(); + DEV_WRITE_GUARDED( x_working ) { assert( !m_working.isSealed() ); @@ -1190,8 +1203,6 @@ h256 Client::importTransaction( Transaction const& _t ) { // the latest block in the client's blockchain. This can throw but // we'll catch the exception at the RPC level. - const_cast< Transaction& >( _t ).checkOutExternalGas( chainParams().externalGasDifficulty ); - // throws in case of error State state; u256 gasBidPrice; @@ -1199,6 +1210,10 @@ h256 Client::importTransaction( Transaction const& _t ) { DEV_GUARDED( m_blockImportMutex ) { state = this->state().createStateReadOnlyCopy(); gasBidPrice = this->gasBidPrice(); + + // We need to check external gas under mutex to be sure about current block bumber + // correctness + const_cast< Transaction& >( _t ).checkOutExternalGas( chainParams(), number() ); } Executive::verifyTransaction( _t, @@ -1255,7 +1270,7 @@ ExecutionResult Client::call( Address const& _from, u256 _value, Address _dest, Transaction t( _value, gasPrice, gas, _dest, _data, nonce ); t.forceSender( _from ); t.forceChainId( chainParams().chainID ); - t.checkOutExternalGas( ~u256( 0 ) ); + t.ignoreExternalGas(); if ( _ff == FudgeFactor::Lenient ) { historicBlock.mutableState().mutableHistoricState().addBalance( _from, ( u256 )( t.gas() * t.gasPrice() + t.value() ) ); @@ -1280,7 +1295,7 @@ ExecutionResult Client::call( Address const& _from, u256 _value, Address _dest, Transaction t( _value, gasPrice, gas, _dest, _data, nonce ); t.forceSender( _from ); t.forceChainId( chainParams().chainID ); - t.checkOutExternalGas( ~u256( 0 ) ); + t.ignoreExternalGas(); if ( _ff == FudgeFactor::Lenient ) temp.mutableState().addBalance( _from, ( u256 )( t.gas() * t.gasPrice() + t.value() ) ); ret = temp.execute( bc().lastBlockHashes(), t, skale::Permanence::Reverted ); diff --git a/libethereum/Client.h b/libethereum/Client.h index d62af01b1..3bf2015c4 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -127,8 +127,6 @@ class Client : public ClientBase, protected Worker { /// Retrieve pending transactions Transactions pending() const override; - Transactions debugGetFutureTransactions() const { return m_tq.debugGetFutureTransactions(); } - /// Queues a block for import. ImportResult queueBlock( bytes const& _block, bool _isSafe = false ); diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 842556765..6b6d530b4 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -23,6 +23,7 @@ */ #include "ClientBase.h" +#include #include #include @@ -89,7 +90,7 @@ std::pair< bool, ExecutionResult > ClientBase::estimateGasStep( int64_t _gas, Bl t = Transaction( _value, _gasPrice, _gas, _data, nonce ); t.forceSender( _from ); t.forceChainId( chainId() ); - t.checkOutExternalGas( ~u256( 0 ) ); + t.ignoreExternalGas(); EnvInfo const env( _latestBlock.info(), bc().lastBlockHashes(), 0, _gas ); // Make a copy of state!! It will be deleted after step! State tempState = _latestBlock.mutableState(); @@ -115,8 +116,12 @@ std::pair< u256, ExecutionResult > ClientBase::estimateGas( Address const& _from int64_t upperBound = _maxGas; if ( upperBound == Invalid256 || upperBound > c_maxGasEstimate ) upperBound = c_maxGasEstimate; - int64_t lowerBound = Transaction::baseGasRequired( !_dest, &_data, - bc().sealEngine()->chainParams().scheduleForBlockNumber( bc().number() ) ); + int64_t lowerBound = + CorrectForkInPowPatch::isEnabled() ? + Transaction::baseGasRequired( !_dest, &_data, + bc().sealEngine()->chainParams().scheduleForBlockNumber( bc().number() ) ) : + Transaction::baseGasRequired( !_dest, &_data, EVMSchedule() ); + Block bk = latestBlock(); if ( upperBound > bk.info().gasLimit() ) { upperBound = bk.info().gasLimit().convert_to< int64_t >(); diff --git a/libethereum/SkaleHost.cpp b/libethereum/SkaleHost.cpp index 6508d8781..01434a060 100644 --- a/libethereum/SkaleHost.cpp +++ b/libethereum/SkaleHost.cpp @@ -653,6 +653,7 @@ void SkaleHost::createBlock( const ConsensusExtFace::transactions_vector& _appro // TODO clear occasionally this cache?! if ( m_m_transaction_cache.find( sha.asArray() ) != m_m_transaction_cache.cend() ) { Transaction t = m_m_transaction_cache.at( sha.asArray() ); + t.checkOutExternalGas( m_client.chainParams(), m_client.number(), true ); out_txns.push_back( t ); LOG( m_debugLogger ) << "Dropping good txn " << sha << std::endl; m_debugTracer.tracepoint( "drop_good" ); @@ -666,7 +667,7 @@ void SkaleHost::createBlock( const ConsensusExtFace::transactions_vector& _appro // ).detach(); } else { Transaction t( data, CheckTransaction::Everything, true ); - t.checkOutExternalGas( m_client.chainParams().externalGasDifficulty ); + t.checkOutExternalGas( m_client.chainParams(), m_client.number() ); out_txns.push_back( t ); LOG( m_debugLogger ) << "Will import consensus-born txn"; m_debugTracer.tracepoint( "import_consensus_born" ); diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index 1645c30c3..de556a0f9 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -29,6 +29,7 @@ #include #include #include +#include using namespace std; using namespace dev; @@ -180,19 +181,32 @@ u256 Transaction::gasPrice() const { } } -void Transaction::checkOutExternalGas( u256 const& _difficulty ) { - assert( _difficulty > 0 ); - if ( !m_externalGasIsChecked && !isInvalid() ) { +void Transaction::checkOutExternalGas( const ChainParams& _cp, uint64_t _bn, bool _force ) { + u256 const& difficulty = _cp.externalGasDifficulty; + assert( difficulty > 0 ); + if ( ( _force || !m_externalGasIsChecked ) && !isInvalid() ) { h256 hash = dev::sha3( sender().ref() ) ^ dev::sha3( nonce() ) ^ dev::sha3( gasPrice() ); if ( !hash ) { hash = h256( 1 ); } - u256 externalGas = ~u256( 0 ) / u256( hash ) / _difficulty; + u256 externalGas = ~u256( 0 ) / u256( hash ) / difficulty; if ( externalGas > 0 ) ctrace << "Mined gas: " << externalGas << endl; - if ( externalGas >= baseGasRequired( ConstantinopleSchedule ) ) { - m_externalGas = externalGas; + + EVMSchedule scheduleForUse = ConstantinopleSchedule; + if ( CorrectForkInPowPatch::isEnabled() ) + scheduleForUse = _cp.scheduleForBlockNumber( _bn ); + + // never call checkOutExternalGas with non-last block + if ( _bn != CorrectForkInPowPatch::getLastBlockNumber() ) { + ctrace << _bn << " != " << CorrectForkInPowPatch::getLastBlockNumber(); + BOOST_THROW_EXCEPTION( std::runtime_error( + "Internal error: checkOutExternalGas() has invalid block number" ) ); } + + if ( externalGas >= baseGasRequired( scheduleForUse ) ) + m_externalGas = externalGas; + m_externalGasIsChecked = true; } } diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 6764bce5a..dffadb347 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -29,6 +29,8 @@ #include #include +#include "ChainParams.h" + namespace dev { namespace eth { @@ -120,7 +122,12 @@ class Transaction : public TransactionBase { u256 gasPrice() const; - void checkOutExternalGas( u256 const& _difficulty ); + void checkOutExternalGas( const ChainParams& _cp, uint64_t _bn, bool _force = false ); + + void ignoreExternalGas() { + m_externalGasIsChecked = true; + m_externalGas = 0; + } private: bool m_externalGasIsChecked = false; diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 6a075bf84..bfeee388f 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -537,14 +537,3 @@ void TransactionQueue::verifierBody() { MICROPROFILE_LEAVE(); } } - -Transactions TransactionQueue::debugGetFutureTransactions() const { - Transactions res; - ReadGuard l( m_lock ); - for ( auto addressAndMap : m_future ) { - for ( auto nonceAndTransaction : addressAndMap.second ) { - res.push_back( nonceAndTransaction.second.transaction ); - } // for nonce - } // for address - return res; -} diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index d089c4004..63ad3ee96 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -123,8 +123,6 @@ class TransactionQueue { template < class... Args > Transactions topTransactionsSync( unsigned _limit, Args... args ); - Transactions debugGetFutureTransactions() const; - /// Get a hash set of transactions in the queue /// @returns A hash set of all transactions in the queue const h256Hash knownTransactions() const; diff --git a/libethereum/ValidationSchemes.cpp b/libethereum/ValidationSchemes.cpp index 75bd07af1..1f7243e31 100644 --- a/libethereum/ValidationSchemes.cpp +++ b/libethereum/ValidationSchemes.cpp @@ -274,6 +274,8 @@ void validateConfigJson( js::mObject const& _obj ) { { "skipInvalidTransactionsPatchTimestamp", { { js::int_type }, JsonFieldPresence::Optional } }, { "precompiledConfigPatchTimestamp", + { { js::int_type }, JsonFieldPresence::Optional } }, + { "correctForkInPowPatchTimestamp", { { js::int_type }, JsonFieldPresence::Optional } } } ); js::mArray const& nodes = sChain.at( "nodes" ).get_array(); diff --git a/libskale/CMakeLists.txt b/libskale/CMakeLists.txt index aec50d6e0..1a0fb8ff1 100644 --- a/libskale/CMakeLists.txt +++ b/libskale/CMakeLists.txt @@ -23,6 +23,7 @@ set(sources PrecompiledConfigPatch.cpp PushZeroPatch.cpp SkipInvalidTransactionsPatch.cpp + CorrectForkInPowPatch.cpp ) set(headers @@ -44,6 +45,7 @@ set(headers PrecompiledConfigPatch.h OverlayFS.h SkipInvalidTransactionsPatch.h + CorrectForkInPowPatch.h ) add_library(skale ${sources} ${headers}) diff --git a/libskale/CorrectForkInPowPatch.cpp b/libskale/CorrectForkInPowPatch.cpp new file mode 100644 index 000000000..aec18bc29 --- /dev/null +++ b/libskale/CorrectForkInPowPatch.cpp @@ -0,0 +1,12 @@ +#include "CorrectForkInPowPatch.h" + +time_t CorrectForkInPowPatch::activationTimestamp; +time_t CorrectForkInPowPatch::lastBlockTimestamp; +unsigned CorrectForkInPowPatch::lastBlockNumber; + +bool CorrectForkInPowPatch::isEnabled() { + if ( activationTimestamp == 0 ) { + return false; + } + return activationTimestamp <= lastBlockTimestamp; +} diff --git a/libskale/CorrectForkInPowPatch.h b/libskale/CorrectForkInPowPatch.h new file mode 100644 index 000000000..94c0f766e --- /dev/null +++ b/libskale/CorrectForkInPowPatch.h @@ -0,0 +1,41 @@ +#ifndef CORRECTFORKINPOWPATCH_H +#define CORRECTFORKINPOWPATCH_H + +#include + +#include + +namespace dev { +namespace eth { +class Client; +} +namespace test { +class TestBlockChain; +class TestOutputHelperFixture; +} // namespace test +} // namespace dev + +/* + * Context: use current, and not Constantinople, fork in Transaction::checkOutExternalGas() + */ +class CorrectForkInPowPatch : public SchainPatch { +public: + static bool isEnabled(); + + static void setTimestamp( time_t _timeStamp ) { + printInfo( __FILE__, _timeStamp ); + activationTimestamp = _timeStamp; + } + + static unsigned getLastBlockNumber() { return lastBlockNumber; } + +private: + friend class dev::eth::Client; + friend class dev::test::TestBlockChain; + friend class dev::test::TestOutputHelperFixture; + static time_t activationTimestamp; + static time_t lastBlockTimestamp; + static unsigned lastBlockNumber; +}; + +#endif // CORRECTFORKINPOWPATCH_H diff --git a/libweb3jsonrpc/Debug.cpp b/libweb3jsonrpc/Debug.cpp index 91cc76bca..0ef884262 100644 --- a/libweb3jsonrpc/Debug.cpp +++ b/libweb3jsonrpc/Debug.cpp @@ -314,10 +314,3 @@ uint64_t Debug::debug_doBlocksDbCompaction() { return boost::chrono::duration_cast< boost::chrono::milliseconds >( t2 - t1 ).count(); } - -Json::Value Debug::debug_getFutureTransactions() { - auto res = toJson( m_eth.debugGetFutureTransactions() ); - for ( auto& t : res ) - t.removeMember( "data" ); - return res; -} diff --git a/libweb3jsonrpc/Debug.h b/libweb3jsonrpc/Debug.h index 6d85b9d7d..f5337001d 100644 --- a/libweb3jsonrpc/Debug.h +++ b/libweb3jsonrpc/Debug.h @@ -60,8 +60,6 @@ class Debug : public DebugFace { virtual uint64_t debug_doStateDbCompaction() override; virtual uint64_t debug_doBlocksDbCompaction() override; - virtual Json::Value debug_getFutureTransactions() override; - private: eth::Client const& m_eth; SkaleDebugInterface* m_debugInterface = nullptr; diff --git a/libweb3jsonrpc/DebugFace.h b/libweb3jsonrpc/DebugFace.h index ade7094b0..ebcc07fe2 100644 --- a/libweb3jsonrpc/DebugFace.h +++ b/libweb3jsonrpc/DebugFace.h @@ -98,10 +98,6 @@ class DebugFace : public ServerInterface< DebugFace > { this->bindAndAddMethod( jsonrpc::Procedure( "debug_doBlocksDbCompaction", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL ), &dev::rpc::DebugFace::debug_doBlocksDbCompactionI ); - - this->bindAndAddMethod( jsonrpc::Procedure( "debug_getFutureTransactions", - jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL ), - &dev::rpc::DebugFace::debug_getFutureTransactionsI ); } inline virtual void debug_accountRangeAtI( const Json::Value& request, Json::Value& response ) { response = this->debug_accountRangeAt( request[0u].asString(), request[1u].asInt(), @@ -183,10 +179,6 @@ class DebugFace : public ServerInterface< DebugFace > { response = this->debug_doBlocksDbCompaction(); } - virtual void debug_getFutureTransactionsI( const Json::Value&, Json::Value& response ) { - response = this->debug_getFutureTransactions(); - } - virtual Json::Value debug_accountRangeAt( const std::string& param1, int param2, const std::string& param3, int param4 ) = 0; virtual Json::Value debug_traceTransaction( @@ -214,8 +206,6 @@ class DebugFace : public ServerInterface< DebugFace > { virtual uint64_t debug_doStateDbCompaction() = 0; virtual uint64_t debug_doBlocksDbCompaction() = 0; - - virtual Json::Value debug_getFutureTransactions() = 0; }; } // namespace rpc diff --git a/test/tools/libtesteth/BlockChainHelper.cpp b/test/tools/libtesteth/BlockChainHelper.cpp index fbdaf71be..757ac6169 100644 --- a/test/tools/libtesteth/BlockChainHelper.cpp +++ b/test/tools/libtesteth/BlockChainHelper.cpp @@ -21,6 +21,8 @@ * that manage block/transaction import and test mining */ +#include + #include #include #include @@ -473,6 +475,10 @@ void TestBlockChain::reset( TestBlock const& _genesisBlock ) { } bool TestBlockChain::addBlock( TestBlock const& _block ) { + + CorrectForkInPowPatch::lastBlockTimestamp = m_blockChain->info().timestamp(); + CorrectForkInPowPatch::lastBlockNumber = m_blockChain->number(); + while ( true ) { try { _block.verify( *this ); // check that block header match TestBlock contents @@ -494,6 +500,10 @@ bool TestBlockChain::addBlock( TestBlock const& _block ) { State st( block.state() ); m_lastBlock.setState( st ); + + CorrectForkInPowPatch::lastBlockTimestamp = m_blockChain->info().timestamp(); + CorrectForkInPowPatch::lastBlockNumber = m_blockChain->number(); + return true; } diff --git a/test/tools/libtesteth/ImportTest.cpp b/test/tools/libtesteth/ImportTest.cpp index 8400ed13c..7ecf2283d 100644 --- a/test/tools/libtesteth/ImportTest.cpp +++ b/test/tools/libtesteth/ImportTest.cpp @@ -111,36 +111,9 @@ void ImportTest::makeBlockchainTestFromStateTest( set< eth::Network > const& _ne TrExpectSection* search2 = &search; checkGeneralTestSectionSearch( exp.get_obj(), stateIndexesToPrint, "", search2 ); throw std::logic_error( "Skale state does not support addresses list" ); - // if (search.second.first.addresses().size() != 0) // if match in - // the expect sections - // // for this tr - // found - // { - // // replace expected mining reward (in state tests it is 0) - // json_spirit::mObject obj = - // fillJsonWithState(search2->second.first, - // search2->second.second); - // for (auto& adr : obj) - // { - // if (adr.first == toHexPrefixed(m_envInfo->author()) && - // adr.second.get_obj().count("balance")) - // { - // u256 expectCoinbaseBalance = - // toInt(adr.second.get_obj()["balance"]); - // expectCoinbaseBalance += blockReward; - // adr.second.get_obj()["balance"] = - // toCompactHexPrefixed(expectCoinbaseBalance); - // } - // } - - // json_spirit::mObject expetSectionObj; - // expetSectionObj["network"] = test::netIdToString(net); - // expetSectionObj["result"] = obj; - // expetSectionArray.push_back(expetSectionObj); - // break; - // } + } // for exp - } // for net + } // for net testObj["expect"] = expetSectionArray; @@ -225,7 +198,6 @@ bytes ImportTest::executeTest( bool _isFilling ) { continue; for ( auto& tr : m_transactions ) { - tr.transaction.checkOutExternalGas( 100 ); Options const& opt = Options::get(); if ( opt.trDataIndex != -1 && opt.trDataIndex != tr.dataInd ) continue; @@ -739,15 +711,6 @@ bool ImportTest::checkGeneralTestSectionSearch( json_spirit::mObject const& _exp _errorTransactions.push_back( i ); } } else if ( _expects.count( "hash" ) ) { - // checking filled state test against client - // BOOST_CHECK_MESSAGE(_expects.at("hash").get_str() == - // toHexPrefixed(tr.postState.globalRoot().asBytes()), - // TestOutputHelper::get().testName() + " on " + - // test::netIdToString(tr.netId) + - // ": Expected another postState hash! expected: " + - // _expects.at("hash").get_str() + " actual: " + - // toHexPrefixed(tr.postState.globalRoot().asBytes()) + - // " in " + trInfo); if ( _expects.count( "logs" ) ) BOOST_CHECK_MESSAGE( _expects.at( "logs" ).get_str() == exportLog( tr.output.second.log() ), diff --git a/test/tools/libtesteth/TestOutputHelper.cpp b/test/tools/libtesteth/TestOutputHelper.cpp index c863fa0fd..f5ec7cdf2 100644 --- a/test/tools/libtesteth/TestOutputHelper.cpp +++ b/test/tools/libtesteth/TestOutputHelper.cpp @@ -20,6 +20,7 @@ * Fixture class for boost output when running testeth */ +#include #include #include #include @@ -102,6 +103,8 @@ void TestOutputHelper::printTestExecStats() { } TestOutputHelperFixture::TestOutputHelperFixture() { TestOutputHelper::get().initTest(); + CorrectForkInPowPatch::lastBlockTimestamp = 1; + CorrectForkInPowPatch::lastBlockNumber = 0; } TestOutputHelperFixture::~TestOutputHelperFixture() { diff --git a/test/unittests/libethereum/SkaleHost.cpp b/test/unittests/libethereum/SkaleHost.cpp index 7870cd7c9..84243ab98 100644 --- a/test/unittests/libethereum/SkaleHost.cpp +++ b/test/unittests/libethereum/SkaleHost.cpp @@ -123,7 +123,7 @@ struct SkaleHostFixture : public TestOutputHelperFixture { chainParams.allowFutureBlocks = true; chainParams.difficulty = chainParams.minimumDifficulty; chainParams.gasLimit = chainParams.maxGasLimit; - chainParams.byzantiumForkBlock = 0; + chainParams.istanbulForkBlock = 0; // 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 @@ -913,7 +913,7 @@ BOOST_AUTO_TEST_CASE( transactionDropReceive // 1st tx Transaction tx1 = tx_from_json( json ); - tx1.checkOutExternalGas( client->chainParams().difficulty ); + tx1.checkOutExternalGas( client->chainParams(), client->number() ); // submit it! tq->import( tx1 ); @@ -970,7 +970,7 @@ BOOST_AUTO_TEST_CASE( transactionDropQueue, // 1st tx Transaction tx1 = tx_from_json( json ); - tx1.checkOutExternalGas( client->chainParams().difficulty ); + tx1.checkOutExternalGas( client->chainParams(), client->number() ); // submit it! tq->import( tx1 ); @@ -1027,7 +1027,7 @@ BOOST_AUTO_TEST_CASE( transactionDropByGasPrice // 1st tx Transaction tx1 = tx_from_json( json ); - tx1.checkOutExternalGas( client->chainParams().difficulty ); + tx1.checkOutExternalGas( client->chainParams(), client->number() ); // submit it! tq->import( tx1 ); @@ -1090,7 +1090,7 @@ BOOST_AUTO_TEST_CASE( transactionDropByGasPriceReceive // 1st tx Transaction tx1 = tx_from_json( json ); - tx1.checkOutExternalGas( client->chainParams().difficulty ); + tx1.checkOutExternalGas( client->chainParams(), client->number() ); RLPStream stream1; tx1.streamRLP( stream1 ); diff --git a/test/unittests/libweb3jsonrpc/WebThreeStubClient.cpp b/test/unittests/libweb3jsonrpc/WebThreeStubClient.cpp index 680bdb57e..acdebbf42 100644 --- a/test/unittests/libweb3jsonrpc/WebThreeStubClient.cpp +++ b/test/unittests/libweb3jsonrpc/WebThreeStubClient.cpp @@ -1344,13 +1344,3 @@ Json::Value WebThreeStubClient::debug_doBlocksDbCompaction() { throw jsonrpc::JsonRpcException( jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString() ); } - -Json::Value WebThreeStubClient::debug_getFutureTransactions() { - Json::Value p; - Json::Value result = this->CallMethod( "debug_getFutureTransactions", p ); - if ( result.isArray() ) - return result; - else - throw jsonrpc::JsonRpcException( - jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString() ); -} diff --git a/test/unittests/libweb3jsonrpc/WebThreeStubClient.h b/test/unittests/libweb3jsonrpc/WebThreeStubClient.h index ad50db93a..5f56db895 100644 --- a/test/unittests/libweb3jsonrpc/WebThreeStubClient.h +++ b/test/unittests/libweb3jsonrpc/WebThreeStubClient.h @@ -57,7 +57,7 @@ class WebThreeStubClient : public jsonrpc::Client { std::string eth_call( const Json::Value& param1, const std::string& param2 ) noexcept( false ); std::string eth_callEIP1898( const Json::Value& param1, const Json::Value& param2 ) noexcept( false ); bool eth_flush() noexcept( false ); - std::string eth_estimateGas( const Json::Value& param1, const std::string& param2 = "" ) noexcept( false ); + std::string eth_estimateGas( const Json::Value& param1, const std::string& param2 = "latest" ) noexcept( false ); Json::Value eth_getBlockByHash( const std::string& param1, bool param2 ) noexcept( false ); Json::Value eth_getBlockByNumber( const std::string& param1, bool param2 ) noexcept( false ); Json::Value eth_getTransactionByHash( const std::string& param1 ) noexcept( false ); @@ -160,7 +160,6 @@ class WebThreeStubClient : public jsonrpc::Client { const Json::Value& param3 ) noexcept( false ); Json::Value debug_doStateDbCompaction() noexcept( false ); Json::Value debug_doBlocksDbCompaction() noexcept( false ); - Json::Value debug_getFutureTransactions() noexcept( false ); }; #endif // JSONRPC_CPP_STUB_WEBTHREESTUBCLIENT_H_ diff --git a/test/unittests/libweb3jsonrpc/jsonrpc.cpp b/test/unittests/libweb3jsonrpc/jsonrpc.cpp index 26e2c893c..0f683f344 100644 --- a/test/unittests/libweb3jsonrpc/jsonrpc.cpp +++ b/test/unittests/libweb3jsonrpc/jsonrpc.cpp @@ -248,7 +248,7 @@ class TestIpcClient : public jsonrpc::IClientConnector { struct JsonRpcFixture : public TestOutputHelperFixture { JsonRpcFixture( const std::string& _config = "", bool _owner = true, bool _deploymentControl = true, bool _generation2 = false, - bool _mtmEnabled = false, bool _isSyncNode = false ) { + bool _mtmEnabled = false, bool _isSyncNode = false, int _emptyBlockIntervalMs = -1 ) { dev::p2p::NetworkPreferences nprefs; ChainParams chainParams; @@ -289,6 +289,9 @@ struct JsonRpcFixture : public TestOutputHelperFixture { // 615 + 1430 is experimentally-derived block size + average extras size chainParams.sChain.dbStorageLimit = 320.5*( 615 + 1430 ); chainParams.sChain.contractStoragePatchTimestamp = 1; + powPatchActivationTimestamp = time(nullptr) + 20; + chainParams.sChain.correctForkInPowPatchTimestamp = powPatchActivationTimestamp; // 10 guessed seconds + chainParams.sChain.emptyBlockIntervalMs = _emptyBlockIntervalMs; // 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 @@ -416,6 +419,7 @@ struct JsonRpcFixture : public TestOutputHelperFixture { unique_ptr< WebThreeStubClient > rpcClient; std::string adminSession; SkaleServerOverride* skale_server_connector; + time_t powPatchActivationTimestamp; }; struct RestrictedAddressFixture : public JsonRpcFixture { @@ -1598,6 +1602,78 @@ BOOST_AUTO_TEST_CASE( call_from_parameter ) { responseString, "0x000000000000000000000000112233445566778899aabbccddeeff0011223344" ); } +BOOST_AUTO_TEST_CASE( simplePoWTransaction ) { + // 1s empty block interval + JsonRpcFixture fixture( "", true, true, false, false, false, 1000 ); + dev::eth::simulateMining( *( fixture.client ), 1 ); + + auto senderAddress = fixture.coinbase.address(); + + Json::Value transact; + transact["from"] = toJS( senderAddress ); + transact["to"] = toJS( senderAddress ); + // 1k + ostringstream ss("0x"); + for(int i=0; i<1024/16; ++i) + ss << "112233445566778899aabbccddeeff11"; + transact["data"] = ss.str(); + + string gasEstimateStr = fixture.rpcClient->eth_estimateGas(transact); + u256 gasEstimate = jsToU256(gasEstimateStr); + + // old estimate before patch + BOOST_REQUIRE_EQUAL(gasEstimate, u256(21000+1024*68)); + + u256 powGasPrice = 0; + u256 correctEstimate = u256(21000+1024*16); + do { + const u256 GAS_PER_HASH = 1; + u256 candidate = h256::random(); + h256 hash = dev::sha3( senderAddress ) ^ dev::sha3( u256( 0 ) ) ^ dev::sha3( candidate ); + u256 externalGas = ~u256( 0 ) / u256( hash ) * GAS_PER_HASH; + if ( externalGas >= correctEstimate && externalGas < correctEstimate + correctEstimate/10 ) { + powGasPrice = candidate; + } + } while ( !powGasPrice ); + // Account balance is too low will mean that PoW didn't work out + transact["gasPrice"] = toJS( powGasPrice ); + + // wait for patch turning on and see how it happens + string txHash; + BlockHeader badInfo, goodInfo; + for(;;) { + string gasEstimateStr = fixture.rpcClient->eth_estimateGas(transact); + u256 gasEstimate = jsToU256(gasEstimateStr); + // old + if(gasEstimate == u256(21000+1024*68)){ + try{ + fixture.rpcClient->eth_sendTransaction( transact ); + BOOST_REQUIRE(false); + } catch(const std::exception& ex) { + assert(string(ex.what()).find("balance is too low") != string::npos); + badInfo = fixture.client->blockInfo(fixture.client->hashFromNumber(LatestBlock)); + dev::eth::mineTransaction( *( fixture.client ), 1 ); // empty block + } // catch + } + // new + else { + BOOST_REQUIRE_EQUAL(gasEstimate, correctEstimate); + txHash = fixture.rpcClient->eth_sendTransaction( transact ); + goodInfo = fixture.client->blockInfo(fixture.client->hashFromNumber(LatestBlock)); + break; + } // else + } // for + + BOOST_REQUIRE_LT(badInfo.timestamp(), fixture.powPatchActivationTimestamp); + BOOST_REQUIRE_GE(goodInfo.timestamp(), fixture.powPatchActivationTimestamp); + BOOST_REQUIRE_EQUAL(badInfo.number()+1, goodInfo.number()); + + dev::eth::mineTransaction( *( fixture.client ), 1 ); + + Json::Value receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE_EQUAL(receipt["status"], "0x1"); +} + BOOST_AUTO_TEST_CASE( transactionWithoutFunds ) { JsonRpcFixture fixture; dev::eth::simulateMining( *( fixture.client ), 1 ); @@ -2878,40 +2954,23 @@ BOOST_AUTO_TEST_CASE( mtm_import_future_txs ) { BOOST_REQUIRE( h1 ); BOOST_REQUIRE_EQUAL( tq->futureSize(), 1); - Json::Value call = fixture.rpcClient->debug_getFutureTransactions(); - BOOST_REQUIRE_EQUAL( call.size(), 1); - h256 h2 = fixture.client->importTransaction( tx3 ); BOOST_REQUIRE( h2 ); BOOST_REQUIRE_EQUAL( tq->futureSize(), 2); - - call = fixture.rpcClient->debug_getFutureTransactions(); - BOOST_REQUIRE_EQUAL( call.size(), 2); - BOOST_REQUIRE_EQUAL( call[0]["from"], string("0x")+txJson["from"].asString() ); - h256 h3 = fixture.client->importTransaction( tx2 ); BOOST_REQUIRE( h3 ); BOOST_REQUIRE_EQUAL( tq->futureSize(), 3); - call = fixture.rpcClient->debug_getFutureTransactions(); - BOOST_REQUIRE_EQUAL( call.size(), 3); - h256 h4 = fixture.client->importTransaction( tx1 ); BOOST_REQUIRE( h4 ); BOOST_REQUIRE_EQUAL( tq->futureSize(), 1); BOOST_REQUIRE_EQUAL( tq->status().current, 3); - call = fixture.rpcClient->debug_getFutureTransactions(); - BOOST_REQUIRE_EQUAL( call.size(), 1); - h256 h5 = fixture.client->importTransaction( tx4 ); BOOST_REQUIRE( h5 ); BOOST_REQUIRE_EQUAL( tq->futureSize(), 0); BOOST_REQUIRE_EQUAL( tq->status().current, 5); - call = fixture.rpcClient->debug_getFutureTransactions(); - BOOST_REQUIRE_EQUAL( call.size(), 0); - fixture.client->skaleHost()->pauseConsensus( false ); } @@ -3332,7 +3391,7 @@ BOOST_AUTO_TEST_CASE( test_transactions ) { Transaction valid( fromHex( "0xf86c808504a817c80083015f90943d7112ee86223baf0a506b9d2a77595cbbba51d1872386f26fc10000801ca0655757fd0650a65a373c48a4dc0f3d6ac5c3831aa0cc2cb863a5909dc6c25f72a071882ee8633466a243c0ea64dadb3120c1ca7a5cc7433c6c0b1c861a85322265" ), CheckTransaction::None ); - valid.checkOutExternalGas( 1 ); + valid.ignoreExternalGas(); client->importTransactionsAsBlock(Transactions{invalid, valid}, 1); @@ -3360,7 +3419,7 @@ BOOST_AUTO_TEST_CASE( test_exceptions ) { Transaction valid( fromHex( "0xf86c808504a817c80083015f90943d7112ee86223baf0a506b9d2a77595cbbba51d1872386f26fc10000801ca0655757fd0650a65a373c48a4dc0f3d6ac5c3831aa0cc2cb863a5909dc6c25f72a071882ee8633466a243c0ea64dadb3120c1ca7a5cc7433c6c0b1c861a85322265" ), CheckTransaction::None ); - valid.checkOutExternalGas( 1 ); + valid.ignoreExternalGas(); client->importTransactionsAsBlock(Transactions{invalid, valid}, 1);