diff --git a/.github/workflows/functional-tests.yml b/.github/workflows/functional-tests.yml index 02ad6deb7..f7356fc36 100644 --- a/.github/workflows/functional-tests.yml +++ b/.github/workflows/functional-tests.yml @@ -24,7 +24,7 @@ with: token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} repository: skalenetwork/skale-ci-integration_tests - ref: v3.18.0 + ref: master submodules: recursive - name: Set up Node uses: actions/setup-node@v3.4.0 diff --git a/VERSION b/VERSION index d21858b11..419f30096 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.18.1 +3.19.0 diff --git a/libconsensus b/libconsensus index 2f7c74374..b1916ed05 160000 --- a/libconsensus +++ b/libconsensus @@ -1 +1 @@ -Subproject commit 2f7c7437465ed3cbbfc914ce9fb0fefe3d096ecb +Subproject commit b1916ed05c3b77f5925662fa591b9a054290d0fd diff --git a/libdevcore/JsonUtils.cpp b/libdevcore/JsonUtils.cpp index 1a6f96c43..660e92170 100644 --- a/libdevcore/JsonUtils.cpp +++ b/libdevcore/JsonUtils.cpp @@ -57,10 +57,12 @@ std::string dev::jsonTypeAsString( json_spirit::Value_type _type ) { } void dev::requireJsonFields( json_spirit::mObject const& _o, std::string const& _config, - std::map< std::string, JsonFieldOptions > const& _validationMap ) { + std::map< std::string, JsonFieldOptions > const& _validationMap, + std::function< bool( const std::string& ) > _callbackForUnexpected ) { // check for unexpected fiedls for ( auto const& field : _o ) { - if ( !_validationMap.count( field.first ) ) { + // _callbackForUnexpected allows to accept ertain unexpected fields + if ( !_validationMap.count( field.first ) && !_callbackForUnexpected( field.first ) ) { std::string const comment = "Unexpected field '" + field.first + "' in config: " + _config; cerror << comment << "\n" diff --git a/libdevcore/JsonUtils.h b/libdevcore/JsonUtils.h index f9bc8dd25..2948e971b 100644 --- a/libdevcore/JsonUtils.h +++ b/libdevcore/JsonUtils.h @@ -41,6 +41,10 @@ using JsonFieldOptions = std::pair< JsonTypeSet, JsonFieldPresence >; @param _validationMap a map with json objects that would be checked. "objName" -> {js::str_type, jsonField::Required} */ -void requireJsonFields( json_spirit::mObject const& _o, std::string const& _configName, - std::map< std::string, JsonFieldOptions > const& _validationMap ); +void requireJsonFields( + json_spirit::mObject const& _o, std::string const& _configName, + std::map< std::string, JsonFieldOptions > const& _validationMap, + std::function< bool( const std::string& ) > _callbackForUnexpected = []( const std::string& ) { + return false; + } ); } // namespace dev diff --git a/libdevcore/LevelDB.h b/libdevcore/LevelDB.h index 514a93648..5aa1f7df0 100644 --- a/libdevcore/LevelDB.h +++ b/libdevcore/LevelDB.h @@ -26,8 +26,8 @@ #include #include -#include "shared_mutex" #include +#include namespace dev::db { class LevelDB : public DatabaseFace { diff --git a/libethashseal/Ethash.cpp b/libethashseal/Ethash.cpp index b8109c7e9..3f893f308 100644 --- a/libethashseal/Ethash.cpp +++ b/libethashseal/Ethash.cpp @@ -130,18 +130,6 @@ void Ethash::verify( Strictness _s, BlockHeader const& _bi, BlockHeader const& _ } } -void Ethash::verifyTransaction( ImportRequirements::value _ir, TransactionBase const& _t, - BlockHeader const& _header, u256 const& _startGasUsed ) const { - SealEngineFace::verifyTransaction( _ir, _t, _header, _startGasUsed ); - - if ( _ir & ImportRequirements::TransactionSignatures ) { - if ( _header.number() >= chainParams().EIP158ForkBlock ) { - uint64_t chainID = chainParams().chainID; - _t.checkChainId( chainID, chainParams().skaleDisableChainIdCheck ); - } // if - } -} - u256 Ethash::childGasLimit( BlockHeader const& _bi, u256 const& _gasFloorTarget ) const { u256 gasFloorTarget = _gasFloorTarget == Invalid256 ? 3141562 : _gasFloorTarget; u256 gasLimit = _bi.gasLimit(); diff --git a/libethashseal/Ethash.h b/libethashseal/Ethash.h index 0fdc6e3dc..92dd767cb 100644 --- a/libethashseal/Ethash.h +++ b/libethashseal/Ethash.h @@ -52,8 +52,6 @@ class Ethash : public SealEngineBase { StringHashMap jsInfo( BlockHeader const& _bi ) const override; void verify( Strictness _s, BlockHeader const& _bi, BlockHeader const& _parent, bytesConstRef _block ) const override; - void verifyTransaction( ImportRequirements::value _ir, TransactionBase const& _t, - BlockHeader const& _header, u256 const& _startGasUsed ) const override; void populateFromParent( BlockHeader& _bi, BlockHeader const& _parent ) const override; strings sealers() const override; diff --git a/libethcore/ChainOperationParams.cpp b/libethcore/ChainOperationParams.cpp index a42ff19cf..4f11713a9 100644 --- a/libethcore/ChainOperationParams.cpp +++ b/libethcore/ChainOperationParams.cpp @@ -22,6 +22,9 @@ */ #include "ChainOperationParams.h" + +#include + #include #include @@ -42,6 +45,14 @@ PrecompiledContract::PrecompiledContract( unsigned _base, unsigned _word, }, _exec, _startingBlock, _allowedAddresses ) {} +time_t SChain::getPatchTimestamp( SchainPatchEnum _patchEnum ) const { + assert( _patchEnum < SchainPatchEnum::PatchesCount ); + if ( _patchEnum < SchainPatchEnum::PatchesCount ) + return _patchTimestamps[static_cast< size_t >( _patchEnum )]; + else + return 0; +} + ChainOperationParams::ChainOperationParams() : m_blockReward( "0x4563918244F40000" ), minGasLimit( 0x1388 ), @@ -52,43 +63,33 @@ ChainOperationParams::ChainOperationParams() difficultyBoundDivisor( 0x0800 ), durationLimit( 0x0d ) {} -EVMSchedule const& ChainOperationParams::scheduleForBlockNumber( u256 const& _blockNumber ) const { - if ( _blockNumber >= skaleUnlimitedForkBlock ) - return SkaleSchedule_Unlimited; - else if ( _blockNumber >= skale1024ForkBlock ) - return SkaleSchedule_1024k; - else if ( _blockNumber >= skale512ForkBlock ) - return SkaleSchedule_512k; - else if ( _blockNumber >= skale256ForkBlock ) - return SkaleSchedule_256k; - else if ( _blockNumber >= skale128ForkBlock ) - return SkaleSchedule_128k; - else if ( _blockNumber >= skale64ForkBlock ) - return SkaleSchedule_64k; - else if ( _blockNumber >= skale32ForkBlock ) - return SkaleSchedule_32k; - else if ( _blockNumber >= skale16ForkBlock ) - return SkaleSchedule_16k; - else if ( _blockNumber >= experimentalForkBlock ) - return ExperimentalSchedule; - else if ( _blockNumber >= istanbulForkBlock ) - return IstanbulSchedule; - else if ( _blockNumber >= constantinopleFixForkBlock ) - return ConstantinopleFixSchedule; - else if ( _blockNumber >= constantinopleForkBlock ) - return ConstantinopleSchedule; - else if ( _blockNumber >= eWASMForkBlock ) - return EWASMSchedule; - else if ( _blockNumber >= byzantiumForkBlock ) - return ByzantiumSchedule; - else if ( _blockNumber >= EIP158ForkBlock ) - return EIP158Schedule; - else if ( _blockNumber >= EIP150ForkBlock ) - return EIP150Schedule; - else if ( _blockNumber >= homesteadForkBlock ) - return HomesteadSchedule; +EVMSchedule const ChainOperationParams::makeEvmSchedule( + time_t _committedBlockTimestamp, u256 const& _workingBlockNumber ) const { + EVMSchedule result; + + // 1 decide by block number + if ( _workingBlockNumber >= experimentalForkBlock ) + result = ExperimentalSchedule; + else if ( _workingBlockNumber >= istanbulForkBlock ) + result = IstanbulSchedule; + else if ( _workingBlockNumber >= constantinopleFixForkBlock ) + result = ConstantinopleFixSchedule; + else if ( _workingBlockNumber >= constantinopleForkBlock ) + result = ConstantinopleSchedule; + else if ( _workingBlockNumber >= byzantiumForkBlock ) + result = ByzantiumSchedule; + else if ( _workingBlockNumber >= EIP158ForkBlock ) + result = EIP158Schedule; + else if ( _workingBlockNumber >= EIP150ForkBlock ) + result = EIP150Schedule; else - return FrontierSchedule; + result = HomesteadSchedule; + + // 2 based on previous - decide by timestamp + if ( PushZeroPatch::isEnabledWhen( _committedBlockTimestamp ) ) + result = PushZeroPatch::makeSchedule( result ); + + return result; } u256 ChainOperationParams::blockReward( EVMSchedule const& _schedule ) const { @@ -98,6 +99,16 @@ u256 ChainOperationParams::blockReward( EVMSchedule const& _schedule ) const { return m_blockReward; } +u256 ChainOperationParams::blockReward( + time_t _committedBlockTimestamp, u256 const& _workingBlockNumber ) const { + EVMSchedule const& schedule{ makeEvmSchedule( _committedBlockTimestamp, _workingBlockNumber ) }; + return blockReward( schedule ); +} + void ChainOperationParams::setBlockReward( u256 const& _newBlockReward ) { m_blockReward = _newBlockReward; } + +time_t ChainOperationParams::getPatchTimestamp( SchainPatchEnum _patchEnum ) const { + return sChain.getPatchTimestamp( _patchEnum ); +} diff --git a/libethcore/ChainOperationParams.h b/libethcore/ChainOperationParams.h index b6e182d65..1383b84b8 100644 --- a/libethcore/ChainOperationParams.h +++ b/libethcore/ChainOperationParams.h @@ -28,6 +28,7 @@ #include #include +#include #include "libethcore/Common.h" #include "libethcore/EVMSchedule.h" @@ -89,6 +90,7 @@ struct NodeInfo { bool syncNode; bool archiveMode; bool syncFromCatchup; + bool testSignatures; NodeInfo( std::string _name = "TestNode", u256 _id = 1, std::string _ip = "127.0.0.11", uint16_t _port = 11111, std::string _ip6 = "::1", uint16_t _port6 = 11111, @@ -106,7 +108,8 @@ struct NodeInfo { "11559732032986387107991004021392285783925812861821192530917403151452391805634", "8495653923123431417604973247489272438418190587263600148770280649306958101930", "4082367875863433681332203403145435568316851327593401208105741076214120093531" }, - bool _syncNode = false, bool _archiveMode = false, bool _syncFromCatchup = false ) { + bool _syncNode = false, bool _archiveMode = false, bool _syncFromCatchup = false, + bool _testSignatures = true ) { name = _name; id = _id; ip = _ip; @@ -121,6 +124,7 @@ struct NodeInfo { syncNode = _syncNode; archiveMode = _archiveMode; syncFromCatchup = _syncFromCatchup; + testSignatures = _testSignatures; } }; @@ -173,16 +177,12 @@ struct SChain { int emptyBlockIntervalMs = -1; int64_t levelDBReopenIntervalMs = -1; size_t t = 1; - time_t revertableFSPatchTimestamp = 0; - time_t contractStoragePatchTimestamp = 0; - time_t contractStorageZeroValuePatchTimestamp = 0; - time_t verifyDaSigsPatchTimestamp = 0; - time_t storageDestructionPatchTimestamp = 0; - time_t powCheckPatchTimestamp = 0; - time_t precompiledConfigPatchTimestamp = 0; - time_t pushZeroPatchTimestamp = 0; - time_t skipInvalidTransactionsPatchTimestamp = 0; - time_t correctForkInPowPatchTimestamp = 0; + + // key is patch name + // public - for tests, don't access it directly + std::vector< time_t > _patchTimestamps = + std::vector< time_t >( static_cast< int >( SchainPatchEnum::PatchesCount ) ); + time_t getPatchTimestamp( SchainPatchEnum _patchEnum ) const; SChain() { name = "TestChain"; @@ -210,8 +210,10 @@ struct ChainOperationParams { u256 m_blockReward; public: - EVMSchedule const& scheduleForBlockNumber( u256 const& _blockNumber ) const; + EVMSchedule const makeEvmSchedule( + time_t _committedBlockTimestamp, u256 const& _workingBlockNumber ) const; u256 blockReward( EVMSchedule const& _schedule ) const; + u256 blockReward( time_t _committedBlockTimestamp, u256 const& _workingBlockNumber ) const; void setBlockReward( u256 const& _newBlockReward ); u256 maximumExtraDataSize = 1024; u256 accountStartNonce = 0; @@ -257,6 +259,25 @@ 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; + + bool isPrecompiled( Address const& _a, u256 const& _blockNumber ) const { + return precompiled.count( _a ) != 0 && _blockNumber >= precompiled.at( _a ).startingBlock(); + } + bigint costOfPrecompiled( + Address const& _a, bytesConstRef _in, u256 const& _blockNumber ) const { + return precompiled.at( _a ).cost( _in, *this, _blockNumber ); + } + std::pair< bool, bytes > executePrecompiled( + Address const& _a, bytesConstRef _in, u256 const& ) const { + return precompiled.at( _a ).execute( _in ); + } + bool precompiledExecutionAllowedFrom( + Address const& _a, Address const& _from, bool _readOnly ) const { + return precompiled.at( _a ).executionAllowedFrom( _from, _readOnly ); + } }; } // namespace eth diff --git a/libethcore/Common.cpp b/libethcore/Common.cpp index 1c9772b55..cb889a51a 100644 --- a/libethcore/Common.cpp +++ b/libethcore/Common.cpp @@ -109,9 +109,26 @@ std::string formatBalance( bigint const& _b ) { } bytes isAddressWhitelistedCallData( Address const& _deployer ) { + // Generating calldata for isAddressWhitelisted contract call: + // 13f44d1 - selector of isAddressWhitelisted function + // 000000000000000000000000 - 12-byte offset for sender address + // _deployer - 20-byte sender address + return fromHex( "13f44d10000000000000000000000000" + _deployer.hex() ); } +bytes isDeploymentAllowedCallData( Address const& _origin, Address const& _deployer ) { + // Generating calldata for isDeploymentAllowed contract call: + // d0f557f4 - selector of isDeploymentAllowed function + // 000000000000000000000000 - 12-byte offset for origin address + // _origin - 20-byte origin address + // 000000000000000000000000 - 12-byte offset for sender address + // _deployer - 20-byte sender address + + return fromHex( "d0f557f4000000000000000000000000" + _origin.hex() + + "000000000000000000000000" + _deployer.hex() ); +} + bytes getMultitransactionCallData() { return fromHex( "0xbad0396e" ); } diff --git a/libethcore/Common.h b/libethcore/Common.h index 980aa7a30..b48b5ffd4 100644 --- a/libethcore/Common.h +++ b/libethcore/Common.h @@ -58,9 +58,12 @@ extern const bytes c_blockhashContractCode; /// Address of the special contract for deployment control extern const Address c_configControllerContractAddress; -/// Formatting call data for deployment control contract +/// Generating call data for deployment control contract bytes isAddressWhitelistedCallData( Address const& _deployer ); +/// Generating calldata for deployment control contract, considering tx origin +bytes isDeploymentAllowedCallData( Address const& _origin, Address const& _deployer ); + /// Formatting call data for multitransaction contract bytes getMultitransactionCallData(); diff --git a/libethcore/EVMSchedule.h b/libethcore/EVMSchedule.h index fe744dd16..b9aec53b5 100644 --- a/libethcore/EVMSchedule.h +++ b/libethcore/EVMSchedule.h @@ -46,6 +46,7 @@ struct EVMSchedule { bool haveExtcodehash = false; bool haveChainID = false; bool haveSelfbalance = false; + bool havePush0 = false; std::array< unsigned, 8 > tierStepGas; unsigned expGas = 10; unsigned expByteGas = 10; @@ -93,7 +94,6 @@ struct EVMSchedule { }; static const EVMSchedule DefaultSchedule = EVMSchedule(); -static const EVMSchedule FrontierSchedule = EVMSchedule( false, false, 21000 ); static const EVMSchedule HomesteadSchedule = EVMSchedule( true, true, 53000 ); static const EVMSchedule EIP150Schedule = [] { @@ -125,12 +125,6 @@ static const EVMSchedule ByzantiumSchedule = [] { return schedule; }(); -static const EVMSchedule EWASMSchedule = [] { - EVMSchedule schedule = ByzantiumSchedule; - schedule.maxCodeSize = std::numeric_limits< unsigned >::max(); - return schedule; -}(); - static const EVMSchedule ConstantinopleSchedule = [] { EVMSchedule schedule = ByzantiumSchedule; schedule.haveCreate2 = true; @@ -174,60 +168,6 @@ static const EVMSchedule ExperimentalSchedule = [] { return schedule; }(); -static const EVMSchedule SkaleSchedule_base = [] { - EVMSchedule schedule = ConstantinopleSchedule; - return schedule; -}(); - -static const EVMSchedule SkaleSchedule_16k = [] { - EVMSchedule schedule = SkaleSchedule_base; - schedule.maxCodeSize = 1024 * 16; - return schedule; -}(); - -static const EVMSchedule SkaleSchedule_32k = [] { - EVMSchedule schedule = SkaleSchedule_base; - schedule.maxCodeSize = 1024 * 32; - return schedule; -}(); - -static const EVMSchedule SkaleSchedule_64k = [] { - EVMSchedule schedule = SkaleSchedule_base; - schedule.maxCodeSize = 1024 * 64; - return schedule; -}(); - -static const EVMSchedule SkaleSchedule_128k = [] { - EVMSchedule schedule = SkaleSchedule_base; - schedule.maxCodeSize = 1024 * 128; - return schedule; -}(); - -static const EVMSchedule SkaleSchedule_256k = [] { - EVMSchedule schedule = SkaleSchedule_base; - schedule.maxCodeSize = 1024 * 256; - return schedule; -}(); - -static const EVMSchedule SkaleSchedule_512k = [] { - EVMSchedule schedule = SkaleSchedule_base; - schedule.maxCodeSize = 1024 * 512; - return schedule; -}(); - -static const EVMSchedule SkaleSchedule_1024k = [] { - EVMSchedule schedule = SkaleSchedule_base; - schedule.maxCodeSize = 1024 * 1024; - return schedule; -}(); - -static const EVMSchedule SkaleSchedule_Unlimited = [] { - EVMSchedule schedule = SkaleSchedule_base; - schedule.maxCodeSize = std::numeric_limits< unsigned >::max(); - return schedule; -}(); - - inline EVMSchedule const& latestScheduleForAccountVersion( u256 const& _version ) { if ( _version == 0 ) return IstanbulSchedule; 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/libethcore/SealEngine.cpp b/libethcore/SealEngine.cpp index c72280397..b9cd03c6f 100644 --- a/libethcore/SealEngine.cpp +++ b/libethcore/SealEngine.cpp @@ -20,7 +20,7 @@ #include "SealEngine.h" #include "TransactionBase.h" -#include +#include #include "../libdevcore/microprofile.h" @@ -94,11 +94,12 @@ void SealEngineFace::populateFromParent( BlockHeader& _bi, BlockHeader const& _p _bi.populateFromParent( _parent ); } -void SealEngineFace::verifyTransaction( ImportRequirements::value _ir, TransactionBase const& _t, - BlockHeader const& _header, u256 const& _gasUsed ) const { +void SealEngineFace::verifyTransaction( ChainOperationParams const& _chainParams, + ImportRequirements::value _ir, TransactionBase const& _t, time_t _committedBlockTimestamp, + BlockHeader const& _header, u256 const& _gasUsed ) { // verifyTransaction is the only place where TransactionBase is used instead of Transaction. u256 gas; - if ( POWCheckPatch::isEnabled() ) { + if ( PowCheckPatch::isEnabledWhen( _committedBlockTimestamp ) ) { // new behavior is to use pow-enabled gas gas = _t.gas(); } else { @@ -108,26 +109,27 @@ void SealEngineFace::verifyTransaction( ImportRequirements::value _ir, Transacti MICROPROFILE_SCOPEI( "SealEngineFace", "verifyTransaction", MP_ORCHID ); if ( ( _ir & ImportRequirements::TransactionSignatures ) && - _header.number() < chainParams().EIP158ForkBlock && _t.isReplayProtected() ) + _header.number() < _chainParams.EIP158ForkBlock && _t.isReplayProtected() ) BOOST_THROW_EXCEPTION( InvalidSignature() ); if ( ( _ir & ImportRequirements::TransactionSignatures ) && - _header.number() < chainParams().experimentalForkBlock && _t.hasZeroSignature() ) + _header.number() < _chainParams.experimentalForkBlock && _t.hasZeroSignature() ) BOOST_THROW_EXCEPTION( InvalidSignature() ); if ( ( _ir & ImportRequirements::TransactionBasic ) && - _header.number() >= chainParams().experimentalForkBlock && _t.hasZeroSignature() && + _header.number() >= _chainParams.experimentalForkBlock && _t.hasZeroSignature() && ( _t.value() != 0 || _t.gasPrice() != 0 || _t.nonce() != 0 ) ) BOOST_THROW_EXCEPTION( InvalidZeroSignatureTransaction() << errinfo_got( static_cast< bigint >( _t.gasPrice() ) ) << errinfo_got( static_cast< bigint >( _t.value() ) ) << errinfo_got( static_cast< bigint >( _t.nonce() ) ) ); - if ( _header.number() >= chainParams().homesteadForkBlock && + if ( _header.number() >= _chainParams.homesteadForkBlock && ( _ir & ImportRequirements::TransactionSignatures ) && _t.hasSignature() ) _t.checkLowS(); - eth::EVMSchedule const& schedule = evmSchedule( _header.number() ); + eth::EVMSchedule const& schedule = + _chainParams.makeEvmSchedule( _committedBlockTimestamp, _header.number() ); // Pre calculate the gas needed for execution if ( ( _ir & ImportRequirements::TransactionBasic ) && _t.baseGasRequired( schedule ) > gas ) @@ -141,6 +143,13 @@ void SealEngineFace::verifyTransaction( ImportRequirements::value _ir, Transacti static_cast< bigint >( _header.gasLimit() - _gasUsed ), static_cast< bigint >( gas ), string( "_gasUsed + (bigint)_t.gas() > _header.gasLimit()" ) ) ); + + if ( _ir & ImportRequirements::TransactionSignatures ) { + if ( _header.number() >= _chainParams.EIP158ForkBlock ) { + uint64_t chainID = _chainParams.chainID; + _t.checkChainId( chainID, _chainParams.skaleDisableChainIdCheck ); + } // if + } } SealEngineFace* SealEngineRegistrar::create( ChainOperationParams const& _params ) { @@ -151,11 +160,13 @@ SealEngineFace* SealEngineRegistrar::create( ChainOperationParams const& _params return ret; } -EVMSchedule const& SealEngineBase::evmSchedule( u256 const& _blockNumber ) const { - return chainParams().scheduleForBlockNumber( _blockNumber ); +EVMSchedule SealEngineBase::evmSchedule( + time_t _committedBlockTimestamp, u256 const& _workingBlockNumber ) const { + return chainParams().makeEvmSchedule( _committedBlockTimestamp, _workingBlockNumber ); } -u256 SealEngineBase::blockReward( u256 const& _blockNumber ) const { - EVMSchedule const& schedule{ evmSchedule( _blockNumber ) }; +u256 SealEngineBase::blockReward( + time_t _committedBlockTimestamp, u256 const& _workingBlockNumber ) const { + EVMSchedule const& schedule{ evmSchedule( _committedBlockTimestamp, _workingBlockNumber ) }; return chainParams().blockReward( schedule ); } diff --git a/libethcore/SealEngine.h b/libethcore/SealEngine.h index b51a87bbf..463ddd9b0 100644 --- a/libethcore/SealEngine.h +++ b/libethcore/SealEngine.h @@ -57,8 +57,9 @@ class SealEngineFace { virtual void verify( Strictness _s, BlockHeader const& _bi, BlockHeader const& _parent = BlockHeader(), bytesConstRef _block = bytesConstRef() ) const; /// Additional verification for transactions in blocks. - virtual void verifyTransaction( ImportRequirements::value _ir, TransactionBase const& _t, - BlockHeader const& _header, u256 const& _gasUsed ) const; + static void verifyTransaction( ChainOperationParams const& _chainParams, + ImportRequirements::value _ir, TransactionBase const& _t, time_t _committedBlockTimestamp, + BlockHeader const& _header, u256 const& _gasUsed ); /// Don't forget to call Super::populateFromParent when subclassing & overriding. virtual void populateFromParent( BlockHeader& _bi, BlockHeader const& _parent ) const; @@ -93,8 +94,10 @@ class SealEngineFace { setChainParams( _params ); return this; } - virtual EVMSchedule const& evmSchedule( u256 const& _blockNumber ) const = 0; - virtual u256 blockReward( u256 const& _blockNumber ) const = 0; + virtual EVMSchedule evmSchedule( + time_t _committedBlockTimestamp, u256 const& _workingBlockNumber ) const = 0; + virtual u256 blockReward( + time_t _committedBlockTimestamp, u256 const& _workingBlockNumber ) const = 0; virtual bool isPrecompiled( Address const& _a, u256 const& _blockNumber ) const { return m_params.precompiled.count( _a ) != 0 && @@ -135,8 +138,10 @@ class SealEngineBase : public SealEngineFace { void onSealGenerated( std::function< void( bytes const& ) > const& _f ) override { m_onSealGenerated = _f; } - EVMSchedule const& evmSchedule( u256 const& _blockNumber ) const override; - u256 blockReward( u256 const& _blockNumber ) const override; + EVMSchedule evmSchedule( + time_t _committedBlockTimestamp, u256 const& _blockNumber ) const override; + u256 blockReward( + time_t _committedBlockTimestamp, u256 const& _workingBlockNumber ) const override; protected: std::function< void( bytes const& s ) > m_onSealGenerated; diff --git a/libethcore/TransactionBase.cpp b/libethcore/TransactionBase.cpp index 198fabab3..55dbeb154 100644 --- a/libethcore/TransactionBase.cpp +++ b/libethcore/TransactionBase.cpp @@ -36,6 +36,58 @@ using namespace std; using namespace dev; using namespace dev::eth; +const size_t MAX_ACCESS_LIST_COUNT = 16; + +std::vector< bytes > validateAccessListRLP( const RLP& _data ) { + if ( !_data.isList() ) + BOOST_THROW_EXCEPTION( InvalidTransactionFormat() + << errinfo_comment( "transaction accessList RLP must be a list" ) ); + auto rlpList = _data.toList(); + if ( rlpList.empty() ) { + // empty accessList, ignore it + return {}; + } + + + for ( const auto& d : rlpList ) { + if ( !d.isList() ) + BOOST_THROW_EXCEPTION( InvalidTransactionFormat() << errinfo_comment( + "transaction accessList RLP must be a list" ) ); + auto accessList = d.toList(); + if ( accessList.size() != 2 ) + BOOST_THROW_EXCEPTION( InvalidTransactionFormat() << errinfo_comment( + "transaction accessList RLP must be a list of size 2" ) ); + if ( !accessList[0].isData() || !accessList[1].isList() ) + BOOST_THROW_EXCEPTION( + InvalidTransactionFormat() << errinfo_comment( + "transaction accessList RLP must be a list of byte array and a list" ) ); + for ( const auto& k : accessList[1].toList() ) + if ( !k.isData() ) + BOOST_THROW_EXCEPTION( + InvalidTransactionFormat() << errinfo_comment( + "transaction storageKeys RLP must be a list of byte array" ) ); + } + + if ( rlpList.size() > MAX_ACCESS_LIST_COUNT ) + BOOST_THROW_EXCEPTION( InvalidTransactionFormat() + << errinfo_comment( "The number of access lists is too large." ) ); + + std::vector< bytes > accessList( rlpList.size() ); + for ( size_t i = 0; i < rlpList.size(); ++i ) { + accessList[i] = rlpList.at( i ).data().toBytes(); + } + + return accessList; +} + +dev::RLPs accessListToRLPs( const std::vector< bytes >& _accessList ) { + dev::RLPs accessList( _accessList.size() ); + for ( size_t i = 0; i < _accessList.size(); ++i ) { + accessList[i] = RLP( _accessList[i] ); + } + return accessList; +} + TransactionBase::TransactionBase( TransactionSkeleton const& _ts, Secret const& _s ) : m_nonce( _ts.nonce ), m_value( _ts.value ), @@ -49,78 +101,245 @@ TransactionBase::TransactionBase( TransactionSkeleton const& _ts, Secret const& sign( _s ); } -TransactionBase::TransactionBase( +void TransactionBase::fillFromBytesLegacy( bytesConstRef _rlpData, CheckTransaction _checkSig, bool _allowInvalid ) { - MICROPROFILE_SCOPEI( "TransactionBase", "ctor", MP_GOLD2 ); + RLP const rlp( _rlpData ); try { - RLP const rlp( _rlpData ); - try { - if ( !rlp.isList() ) - BOOST_THROW_EXCEPTION( InvalidTransactionFormat() - << errinfo_comment( "transaction RLP must be a list" ) ); - - m_nonce = rlp[0].toInt< u256 >(); - m_gasPrice = rlp[1].toInt< u256 >(); - m_gas = rlp[2].toInt< u256 >(); - if ( !rlp[3].isData() ) - BOOST_THROW_EXCEPTION( InvalidTransactionFormat() - << errinfo_comment( "recepient RLP must be a byte array" ) ); - m_type = rlp[3].isEmpty() ? ContractCreation : MessageCall; - m_receiveAddress = - rlp[3].isEmpty() ? Address() : rlp[3].toHash< Address >( RLP::VeryStrict ); - m_value = rlp[4].toInt< u256 >(); - - if ( !rlp[5].isData() ) - BOOST_THROW_EXCEPTION( InvalidTransactionFormat() << errinfo_comment( - "transaction data RLP must be an array" ) ); - - m_data = rlp[5].toBytes(); - - u256 const v = rlp[6].toInt< u256 >(); - h256 const r = rlp[7].toInt< u256 >(); - h256 const s = rlp[8].toInt< u256 >(); - - if ( isZeroSignature( r, s ) ) { - m_chainId = static_cast< uint64_t >( v ); - m_vrs = SignatureStruct{ r, s, 0 }; - } else { - if ( v > 36 ) { - auto const chainId = ( v - 35 ) / 2; - if ( chainId > std::numeric_limits< uint64_t >::max() ) - BOOST_THROW_EXCEPTION( InvalidSignature() ); - m_chainId = static_cast< uint64_t >( chainId ); - } else if ( v != 27 && v != 28 ) + if ( !rlp.isList() ) + BOOST_THROW_EXCEPTION( + InvalidTransactionFormat() << errinfo_comment( "transaction RLP must be a list" ) ); + + m_nonce = rlp[0].toInt< u256 >(); + m_gasPrice = rlp[1].toInt< u256 >(); + m_gas = rlp[2].toInt< u256 >(); + if ( !rlp[3].isData() ) + BOOST_THROW_EXCEPTION( InvalidTransactionFormat() + << errinfo_comment( "recipient RLP must be a byte array" ) ); + m_type = rlp[3].isEmpty() ? ContractCreation : MessageCall; + m_receiveAddress = + rlp[3].isEmpty() ? Address() : rlp[3].toHash< Address >( RLP::VeryStrict ); + m_value = rlp[4].toInt< u256 >(); + + if ( !rlp[5].isData() ) + BOOST_THROW_EXCEPTION( InvalidTransactionFormat() + << errinfo_comment( "transaction data RLP must be an array" ) ); + + m_data = rlp[5].toBytes(); + + u256 const v = rlp[6].toInt< u256 >(); + h256 const r = rlp[7].toInt< u256 >(); + h256 const s = rlp[8].toInt< u256 >(); + + if ( isZeroSignature( r, s ) ) { + m_chainId = static_cast< uint64_t >( v ); + m_vrs = SignatureStruct{ r, s, 0 }; + } else { + if ( v > 36 ) { + auto const chainId = ( v - 35 ) / 2; + if ( chainId > std::numeric_limits< uint64_t >::max() ) BOOST_THROW_EXCEPTION( InvalidSignature() ); - // else leave m_chainId as is (unitialized) + m_chainId = static_cast< uint64_t >( chainId ); + } else if ( v != 27 && v != 28 ) + BOOST_THROW_EXCEPTION( InvalidSignature() ); + // else leave m_chainId as is (unitialized) - auto const recoveryID = m_chainId.has_value() ? - _byte_{ v - ( u256{ *m_chainId } * 2 + 35 ) } : - _byte_{ v - 27 }; - m_vrs = SignatureStruct{ r, s, recoveryID }; + auto const recoveryID = m_chainId.has_value() ? + _byte_{ v - ( u256{ *m_chainId } * 2 + 35 ) } : + _byte_{ v - 27 }; + m_vrs = SignatureStruct{ r, s, recoveryID }; - if ( _checkSig >= CheckTransaction::Cheap && !m_vrs->isValid() ) - BOOST_THROW_EXCEPTION( InvalidSignature() ); - } - - if ( _checkSig == CheckTransaction::Everything ) - m_sender = sender(); - - if ( rlp.itemCount() > 9 ) - BOOST_THROW_EXCEPTION( InvalidTransactionFormat() << errinfo_comment( - "too many fields in the transaction RLP" ) ); - // XXX Strange "catch"-s %) - - } catch ( Exception& _e ) { - _e << errinfo_name( - "invalid transaction format: " + toString( rlp ) + " RLP: " + toHex( rlp.data() ) ); - m_type = Type::Invalid; - m_rawData = _rlpData.toBytes(); - - if ( !_allowInvalid ) - throw; - else { - cwarn << _e.what(); - } + if ( _checkSig >= CheckTransaction::Cheap && !m_vrs->isValid() ) + BOOST_THROW_EXCEPTION( InvalidSignature() ); + } + + if ( _checkSig == CheckTransaction::Everything ) + m_sender = sender(); + + m_txType = TransactionType::Legacy; + + if ( rlp.itemCount() > 9 ) + BOOST_THROW_EXCEPTION( InvalidTransactionFormat() + << errinfo_comment( "too many fields in the transaction RLP" ) ); + // XXX Strange "catch"-s %) + } catch ( Exception& _e ) { + _e << errinfo_name( + "invalid transaction format: " + toString( rlp ) + " RLP: " + toHex( rlp.data() ) ); + m_type = Type::Invalid; + m_rawData = _rlpData.toBytes(); + + if ( !_allowInvalid ) + throw; + else { + cwarn << _e.what(); + } + } +} + +void TransactionBase::fillFromBytesType1( + bytesConstRef _rlpData, CheckTransaction _checkSig, bool _allowInvalid ) { + bytes croppedRlp( _rlpData.begin() + 1, _rlpData.end() ); + RLP const rlp( croppedRlp ); + try { + if ( !rlp.isList() ) + BOOST_THROW_EXCEPTION( + InvalidTransactionFormat() << errinfo_comment( "transaction RLP must be a list" ) ); + + m_chainId = rlp[0].toInt< u256 >(); + m_nonce = rlp[1].toInt< u256 >(); + m_gasPrice = rlp[2].toInt< u256 >(); + m_gas = rlp[3].toInt< u256 >(); + + if ( !rlp[4].isData() ) + BOOST_THROW_EXCEPTION( InvalidTransactionFormat() + << errinfo_comment( "recipient RLP must be a byte array" ) ); + m_type = rlp[4].isEmpty() ? ContractCreation : MessageCall; + m_receiveAddress = + rlp[4].isEmpty() ? Address() : rlp[4].toHash< Address >( RLP::VeryStrict ); + m_value = rlp[5].toInt< u256 >(); + + if ( !rlp[6].isData() ) + BOOST_THROW_EXCEPTION( InvalidTransactionFormat() + << errinfo_comment( "transaction data RLP must be an array" ) ); + + m_data = rlp[6].toBytes(); + + m_accessList = validateAccessListRLP( rlp[7] ); + + bool const yParity = rlp[8].toInt< uint8_t >(); + h256 const r = rlp[9].toInt< u256 >(); + h256 const s = rlp[10].toInt< u256 >(); + + m_vrs = SignatureStruct{ r, s, yParity }; + + if ( _checkSig >= CheckTransaction::Cheap && !m_vrs->isValid() ) + BOOST_THROW_EXCEPTION( InvalidSignature() ); + + m_txType = TransactionType::Type1; + + if ( _checkSig == CheckTransaction::Everything ) + m_sender = sender(); + + if ( rlp.itemCount() > 11 ) + BOOST_THROW_EXCEPTION( InvalidTransactionFormat() + << errinfo_comment( "too many fields in the transaction RLP" ) ); + } catch ( Exception& _e ) { + _e << errinfo_name( + "invalid transaction format: " + toString( rlp ) + " RLP: " + toHex( rlp.data() ) ); + m_type = Type::Invalid; + m_rawData = _rlpData.toBytes(); + + if ( !_allowInvalid ) + throw; + else { + cwarn << _e.what(); + } + } +} + +void TransactionBase::fillFromBytesType2( + bytesConstRef _rlpData, CheckTransaction _checkSig, bool _allowInvalid ) { + bytes croppedRlp( _rlpData.begin() + 1, _rlpData.end() ); + RLP const rlp( croppedRlp ); + try { + if ( !rlp.isList() ) + BOOST_THROW_EXCEPTION( + InvalidTransactionFormat() << errinfo_comment( "transaction RLP must be a list" ) ); + + m_chainId = rlp[0].toInt< u256 >(); + m_nonce = rlp[1].toInt< u256 >(); + m_maxPriorityFeePerGas = rlp[2].toInt< u256 >(); + m_maxFeePerGas = rlp[3].toInt< u256 >(); + if ( m_maxPriorityFeePerGas > m_maxPriorityFeePerGas ) + BOOST_THROW_EXCEPTION( InvalidTransactionFormat() << errinfo_comment( + "maxFeePerGas cannot be less than maxPriorityFeePerGas (The " + "total must be the larger of the two)" ) ); + // set m_gasPrice as SKALE ignores priority fees + m_gasPrice = m_maxFeePerGas; + m_gas = rlp[4].toInt< u256 >(); + + if ( !rlp[5].isData() ) + BOOST_THROW_EXCEPTION( InvalidTransactionFormat() + << errinfo_comment( "recipient RLP must be a byte array" ) ); + m_type = rlp[5].isEmpty() ? ContractCreation : MessageCall; + m_receiveAddress = + rlp[5].isEmpty() ? Address() : rlp[5].toHash< Address >( RLP::VeryStrict ); + m_value = rlp[6].toInt< u256 >(); + + if ( !rlp[7].isData() ) + BOOST_THROW_EXCEPTION( InvalidTransactionFormat() + << errinfo_comment( "transaction data RLP must be an array" ) ); + + m_data = rlp[7].toBytes(); + + m_accessList = validateAccessListRLP( rlp[8] ); + + bool const yParity = rlp[9].toInt< uint8_t >(); + h256 const r = rlp[10].toInt< u256 >(); + h256 const s = rlp[11].toInt< u256 >(); + + m_vrs = SignatureStruct{ r, s, yParity }; + + if ( _checkSig >= CheckTransaction::Cheap && !m_vrs->isValid() ) + BOOST_THROW_EXCEPTION( InvalidSignature() ); + + m_txType = TransactionType::Type2; + + if ( _checkSig == CheckTransaction::Everything ) + m_sender = sender(); + + if ( rlp.itemCount() > 12 ) + BOOST_THROW_EXCEPTION( InvalidTransactionFormat() + << errinfo_comment( "too many fields in the transaction RLP" ) ); + } catch ( Exception& _e ) { + _e << errinfo_name( + "invalid transaction format: " + toString( rlp ) + " RLP: " + toHex( rlp.data() ) ); + m_type = Type::Invalid; + m_rawData = _rlpData.toBytes(); + + if ( !_allowInvalid ) + throw; + else { + cwarn << _e.what(); + } + } +} + +void TransactionBase::fillFromBytesByType( bytesConstRef _rlpData, CheckTransaction _checkSig, + bool _allowInvalid, TransactionType _type ) { + switch ( _type ) { + case TransactionType::Legacy: + fillFromBytesLegacy( _rlpData, _checkSig, _allowInvalid ); + break; + case TransactionType::Type1: + fillFromBytesType1( _rlpData, _checkSig, _allowInvalid ); + break; + case TransactionType::Type2: + fillFromBytesType2( _rlpData, _checkSig, _allowInvalid ); + break; + default: + BOOST_THROW_EXCEPTION( + InvalidTransactionFormat() << errinfo_comment( + "transaction format doesn't correspond to any of the supported formats" ) ); + } +} + +TransactionType TransactionBase::getTransactionType( bytesConstRef _rlp ) { + if ( _rlp.empty() ) + return TransactionType::Legacy; + if ( _rlp[0] > 2 ) + return TransactionType::Legacy; + return TransactionType( _rlp[0] ); +} + +TransactionBase::TransactionBase( + bytesConstRef _rlpData, CheckTransaction _checkSig, bool _allowInvalid, bool _eip1559Enabled ) { + MICROPROFILE_SCOPEI( "TransactionBase", "ctor", MP_GOLD2 ); + try { + if ( _eip1559Enabled ) { + TransactionType txnType = getTransactionType( _rlpData ); + fillFromBytesByType( _rlpData, _checkSig, _allowInvalid, txnType ); + } else { + fillFromBytesLegacy( _rlpData, _checkSig, _allowInvalid ); } } catch ( ... ) { m_type = Type::Invalid; @@ -176,15 +395,8 @@ void TransactionBase::sign( Secret const& _priv ) { m_vrs = sigStruct; } -void TransactionBase::streamRLP( RLPStream& _s, IncludeSignature _sig, bool _forEip155hash ) const { - if ( isInvalid() ) { - _s.appendRaw( m_rawData ); - return; - } - - if ( m_type == NullTransaction ) - return; - +void TransactionBase::streamLegacyTransaction( + RLPStream& _s, IncludeSignature _sig, bool _forEip155hash ) const { _s.appendList( ( _sig || _forEip155hash ? 3 : 0 ) + 6 ); _s << m_nonce << m_gasPrice << m_gas; if ( m_type == MessageCall ) @@ -208,6 +420,64 @@ void TransactionBase::streamRLP( RLPStream& _s, IncludeSignature _sig, bool _for _s << *m_chainId << 0 << 0; } +void TransactionBase::streamType1Transaction( RLPStream& _s, IncludeSignature _sig ) const { + _s.appendList( ( _sig ? 3 : 0 ) + 8 ); + if ( !m_chainId.has_value() ) + BOOST_THROW_EXCEPTION( InvalidTransactionFormat() ); + _s << *m_chainId << m_nonce << m_gasPrice << m_gas; + if ( m_type == MessageCall ) + _s << m_receiveAddress; + else + _s << ""; + _s << m_value << m_data; + + _s << accessListToRLPs( m_accessList ); + + if ( _sig ) + _s << ( u256 ) m_vrs->v << ( u256 ) m_vrs->r << ( u256 ) m_vrs->s; +} + +void TransactionBase::streamType2Transaction( RLPStream& _s, IncludeSignature _sig ) const { + _s.appendList( ( _sig ? 3 : 0 ) + 9 ); + if ( !m_chainId.has_value() ) + BOOST_THROW_EXCEPTION( InvalidTransactionFormat() ); + _s << *m_chainId << m_nonce << m_maxPriorityFeePerGas << m_maxFeePerGas << m_gas; + if ( m_type == MessageCall ) + _s << m_receiveAddress; + else + _s << ""; + _s << m_value << m_data; + + _s << accessListToRLPs( m_accessList ); + + if ( _sig ) + _s << ( u256 ) m_vrs->v << ( u256 ) m_vrs->r << ( u256 ) m_vrs->s; +} + +void TransactionBase::streamRLP( RLPStream& _s, IncludeSignature _sig, bool _forEip155hash ) const { + if ( isInvalid() ) { + _s.appendRaw( m_rawData ); + return; + } + + if ( m_type == NullTransaction ) + return; + + switch ( m_txType ) { + case TransactionType::Legacy: + streamLegacyTransaction( _s, _sig, _forEip155hash ); + break; + case TransactionType::Type1: + streamType1Transaction( _s, _sig ); + break; + case TransactionType::Type2: + streamType2Transaction( _s, _sig ); + break; + default: + break; + } +} + static const u256 c_secp256k1n( "115792089237316195423570985008687907852837564279074904382605163141518161494337" ); @@ -247,10 +517,20 @@ h256 TransactionBase::sha3( IncludeSignature _sig ) const { MICROPROFILE_SCOPEI( "TransactionBase", "sha3", MP_KHAKI2 ); - RLPStream s; - streamRLP( s, _sig, !isInvalid() && isReplayProtected() && _sig == WithoutSignature ); + dev::bytes input; + if ( !isInvalid() ) { + RLPStream s; + streamRLP( s, _sig, !isInvalid() && isReplayProtected() && _sig == WithoutSignature ); + + input = s.out(); + if ( m_txType != TransactionType::Legacy ) + input.insert( input.begin(), m_txType ); + } else { + RLP data( m_rawData ); + input = dev::bytes( data.payload().begin(), data.payload().end() ); + } - auto ret = dev::sha3( s.out() ); + auto ret = dev::sha3( input ); if ( _sig == WithSignature ) m_hashWith = ret; return ret; @@ -277,3 +557,10 @@ u256 TransactionBase::gas() const { u256 TransactionBase::nonPowGas() const { return m_gas; } + +bytesConstRef dev::eth::bytesRefFromTransactionRlp( const RLP& _rlp ) { + if ( _rlp.isList() ) + return _rlp.data(); + else + return _rlp.payload(); +} diff --git a/libethcore/TransactionBase.h b/libethcore/TransactionBase.h index 2b93c6bd2..36a3cd192 100644 --- a/libethcore/TransactionBase.h +++ b/libethcore/TransactionBase.h @@ -40,6 +40,8 @@ enum IncludeSignature { enum class CheckTransaction { None, Cheap, Everything }; +enum TransactionType { Legacy, Type1, Type2 }; + /// Encodes a transaction, ready to be exported to or freshly imported from RLP. class TransactionBase { public: @@ -96,13 +98,13 @@ class TransactionBase { m_type( ContractCreation ) {} /// Constructs a transaction from the given RLP. - explicit TransactionBase( - bytesConstRef _rlp, CheckTransaction _checkSig, bool _allowInvalid = false ); + explicit TransactionBase( bytesConstRef _rlp, CheckTransaction _checkSig, + bool _allowInvalid = false, bool _eip1559Enabled = false ); /// Constructs a transaction from the given RLP. - explicit TransactionBase( - bytes const& _rlp, CheckTransaction _checkSig, bool _allowInvalid = false ) - : TransactionBase( &_rlp, _checkSig, _allowInvalid ) {} + explicit TransactionBase( bytes const& _rlp, CheckTransaction _checkSig, + bool _allowInvalid = false, bool _eip1559Enabled = false ) + : TransactionBase( &_rlp, _checkSig, _allowInvalid, _eip1559Enabled ) {} TransactionBase( TransactionBase const& ) = default; @@ -145,17 +147,14 @@ class TransactionBase { /// @returns true if transaction is contract-creation. bool isCreation() const { return m_type == ContractCreation; } - /// Serialises this transaction to an RLPStream. - /// @throws TransactionIsUnsigned if including signature was requested but it was not - /// initialized - void streamRLP( - RLPStream& _s, IncludeSignature _sig = WithSignature, bool _forEip155hash = false ) const; - /// @returns the RLP serialisation of this transaction. - bytes rlp( IncludeSignature _sig = WithSignature ) const { + bytes toBytes( IncludeSignature _sig = WithSignature, bool _forEip155hash = false ) const { RLPStream s; - streamRLP( s, _sig ); - return s.out(); + streamRLP( s, _sig, _forEip155hash ); + bytes output = s.out(); + if ( m_txType != TransactionType::Legacy ) + output.insert( output.begin(), m_txType ); + return output; } /// @returns the SHA3 hash of the RLP serialisation of this transaction. @@ -249,6 +248,14 @@ class TransactionBase { bool isInvalid() const { return m_type == Type::Invalid; } + TransactionType txType() const { return m_txType; } + + std::vector< bytes > accessList() const { return m_accessList; } + + u256 maxPriorityFeePerGas() const { return m_maxPriorityFeePerGas; } + + u256 maxFeePerGas() const { return m_maxFeePerGas; } + /// Get the fee associated for a transaction with the given data. static int64_t baseGasRequired( bool _contractCreation, bytesConstRef _data, EVMSchedule const& _es ); @@ -285,9 +292,39 @@ class TransactionBase { bytes m_data; ///< The data associated with the transaction, or the initialiser if it's a ///< creation transaction. bytes m_rawData; + std::vector< bytes > m_accessList; ///< The access list. see more + ///< https://eips.ethereum.org/EIPS/eip-2930. Not valid for + ///< legacy txns + u256 m_maxPriorityFeePerGas; ///< The maximum priority fee per gas. Only valid for type2 txns + u256 m_maxFeePerGas; ///< The maximum fee per gas. Only valid for type2 txns + + TransactionType m_txType = TransactionType::Legacy; Counter< TransactionBase > c; +private: + /// Serialises this transaction to an RLPStream. + /// @throws TransactionIsUnsigned if including signature was requested but it was not + /// initialized + void streamRLP( + RLPStream& _s, IncludeSignature _sig = WithSignature, bool _forEip155hash = false ) const; + + static TransactionType getTransactionType( bytesConstRef _rlp ); + + /// Constructs a transaction from the given RLP and transaction type. + void fillFromBytesByType( bytesConstRef _rlpData, CheckTransaction _checkSig, + bool _allowInvalid, TransactionType _type ); + void fillFromBytesLegacy( + bytesConstRef _rlpData, CheckTransaction _checkSig, bool _allowInvalid ); + void fillFromBytesType1( + bytesConstRef _rlpData, CheckTransaction _checkSig, bool _allowInvalid ); + void fillFromBytesType2( + bytesConstRef _rlpData, CheckTransaction _checkSig, bool _allowInvalid ); + + void streamLegacyTransaction( RLPStream& _s, IncludeSignature _sig, bool _forEip155hash ) const; + void streamType1Transaction( RLPStream& _s, IncludeSignature _sig ) const; + void streamType2Transaction( RLPStream& _s, IncludeSignature _sig ) const; + public: mutable int64_t verifiedOn = -1; // on which block it was imported @@ -321,5 +358,7 @@ inline std::ostream& operator<<( std::ostream& _out, TransactionBase const& _t ) return _out; } +extern bytesConstRef bytesRefFromTransactionRlp( const RLP& _rlp ); + } // namespace eth } // namespace dev diff --git a/libethereum/BasicGasPricer.h b/libethereum/BasicGasPricer.h index 49555aac9..9585c85c7 100644 --- a/libethereum/BasicGasPricer.h +++ b/libethereum/BasicGasPricer.h @@ -52,7 +52,8 @@ class BasicGasPricer : public GasPricer { } u256 ask( Block const& ) const override { return m_weiPerRef * m_refsPerBlock / m_gasPerBlock; } - u256 bid( TransactionPriority _p = TransactionPriority::Medium ) const override { + u256 bid( unsigned = dev::eth::LatestBlock, + TransactionPriority _p = TransactionPriority::Medium ) const override { return m_octiles[( int ) _p] > 0 ? m_octiles[( int ) _p] : ( m_weiPerRef * m_refsPerBlock / m_gasPerBlock ); } diff --git a/libethereum/Block.cpp b/libethereum/Block.cpp index 9198a9daa..f9de3c7e7 100644 --- a/libethereum/Block.cpp +++ b/libethereum/Block.cpp @@ -351,7 +351,8 @@ pair< TransactionReceipts, bool > Block::sync( // caller if we hit the limit for ( Transaction& transaction : transactions ) { - transaction.checkOutExternalGas( _bc.chainParams(), _bc.number() ); + transaction.checkOutExternalGas( + _bc.chainParams(), _bc.info().timestamp(), _bc.number(), false ); } assert( _bc.currentHash() == m_currentBlock.parentHash() ); @@ -484,7 +485,7 @@ tuple< TransactionReceipts, unsigned > Block::syncEveryone( LOG( m_logger ) << "Transaction " << tr.sha3() << " WouldNotBeInBlock: gasPrice " << tr.gasPrice() << " < " << _gasPrice; - if ( SkipInvalidTransactionsPatch::isEnabled() ) { + if ( SkipInvalidTransactionsPatch::isEnabledInWorkingBlock() ) { // Add to the user-originated transactions that we've executed. m_transactions.push_back( tr ); m_transactionSet.insert( tr.sha3() ); @@ -508,7 +509,7 @@ tuple< TransactionReceipts, unsigned > Block::syncEveryone( ExecutionResult res = execute( _bc.lastBlockHashes(), tr, Permanence::Committed, OnOpFunc() ); - if ( !SkipInvalidTransactionsPatch::isEnabled() || + if ( !SkipInvalidTransactionsPatch::isEnabledInWorkingBlock() || res.excepted != TransactionException::WouldNotBeInBlock ) { receipts.push_back( m_receipts.back() ); @@ -631,7 +632,8 @@ 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(), _bc.number() ); + const_cast< Transaction& >( tr ).checkOutExternalGas( + _bc.chainParams(), _bc.info().timestamp(), _bc.number(), false ); execute( _bc.lastBlockHashes(), tr ); // cerr << "Now: " // << "State #" << state().getNonce( tr.from() ) << endl; @@ -762,7 +764,8 @@ u256 Block::enact( VerifiedBlockRef const& _block, BlockChain const& _bc ) { assert( _bc.sealEngine() ); DEV_TIMED_ABOVE( "applyRewards", 500 ) - applyRewards( rewarded, _bc.sealEngine()->blockReward( m_currentBlock.number() ) ); + applyRewards( rewarded, + _bc.sealEngine()->blockReward( previousInfo().timestamp(), m_currentBlock.number() ) ); if ( m_currentBlock.gasUsed() != gasUsed() ) { // Do not commit changes of state @@ -812,14 +815,15 @@ ExecutionResult Block::executeHistoricCall( LastBlockHashesFace const& _lh, Tran u256 const gasUsed = _transactionIndex ? receipt( _transactionIndex - 1 ).cumulativeGasUsed() : 0; - EnvInfo const envInfo{ info(), _lh, gasUsed, m_sealEngine->chainParams().chainID }; + EnvInfo const envInfo( info(), _lh, this->previousInfo().timestamp(), gasUsed, + m_sealEngine->chainParams().chainID ); if ( _tracer ) { try { HistoricState stateBefore( m_state.mutableHistoricState() ); - auto resultReceipt = m_state.mutableHistoricState().execute( - envInfo, *m_sealEngine, _t, skale::Permanence::Uncommitted, onOp ); + auto resultReceipt = m_state.mutableHistoricState().execute( envInfo, + m_sealEngine->chainParams(), _t, skale::Permanence::Uncommitted, onOp ); _tracer->finalizeAndPrintTrace( resultReceipt.first, stateBefore, m_state.mutableHistoricState() ); @@ -834,7 +838,7 @@ ExecutionResult Block::executeHistoricCall( LastBlockHashesFace const& _lh, Tran } } else { auto resultReceipt = m_state.mutableHistoricState().execute( - envInfo, *m_sealEngine, _t, skale::Permanence::Reverted, onOp ); + envInfo, m_sealEngine->chainParams(), _t, skale::Permanence::Reverted, onOp ); return resultReceipt.first; } } catch ( std::exception& e ) { @@ -866,7 +870,8 @@ ExecutionResult Block::execute( State stateSnapshot = _p != Permanence::Reverted ? m_state.createStateModifyCopyAndPassLock() : m_state; - EnvInfo envInfo = EnvInfo( info(), _lh, gasUsed(), m_sealEngine->chainParams().chainID ); + EnvInfo envInfo = EnvInfo( + info(), _lh, previousInfo().timestamp(), gasUsed(), m_sealEngine->chainParams().chainID ); // "bad" transaction receipt for failed transactions TransactionReceipt const null_receipt = @@ -881,7 +886,8 @@ ExecutionResult Block::execute( if ( _t.isInvalid() ) throw -1; // will catch below - resultReceipt = stateSnapshot.execute( envInfo, *m_sealEngine, _t, _p, _onOp ); + resultReceipt = + stateSnapshot.execute( envInfo, m_sealEngine->chainParams(), _t, _p, _onOp ); // use fake receipt created above if execution throws!! } catch ( const TransactionException& ex ) { @@ -905,7 +911,7 @@ ExecutionResult Block::execute( if ( _p == Permanence::Committed || _p == Permanence::CommittedWithoutState || _p == Permanence::Uncommitted ) { // Add to the user-originated transactions that we've executed. - if ( !SkipInvalidTransactionsPatch::isEnabled() || + if ( !SkipInvalidTransactionsPatch::isEnabledWhen( previousInfo().timestamp() ) || resultReceipt.first.excepted != TransactionException::WouldNotBeInBlock ) { m_transactions.push_back( _t ); m_receipts.push_back( resultReceipt.second ); @@ -950,13 +956,15 @@ void Block::updateBlockhashContract() { if ( m_state.code( c_blockhashContractAddress ) != c_blockhashContractCode ) { State state = m_state.createStateModifyCopy(); state.setCode( c_blockhashContractAddress, bytes( c_blockhashContractCode ), - m_sealEngine->evmSchedule( blockNumber ).accountVersion ); + m_sealEngine->evmSchedule( this->m_previousBlock.timestamp(), blockNumber ) + .accountVersion ); state.commit( dev::eth::CommitBehaviour::KeepEmptyAccounts ); } } else { m_state.createContract( c_blockhashContractAddress ); m_state.setCode( c_blockhashContractAddress, bytes( c_blockhashContractCode ), - m_sealEngine->evmSchedule( blockNumber ).accountVersion ); + m_sealEngine->evmSchedule( this->m_previousBlock.timestamp(), blockNumber ) + .accountVersion ); m_state.commit( dev::eth::CommitBehaviour::KeepEmptyAccounts ); } } @@ -1009,11 +1017,16 @@ void Block::commitToSeal( receipt( i ).streamRLP( receiptrlp ); receiptsMap.insert( std::make_pair( k.out(), receiptrlp.out() ) ); - RLPStream txrlp; - m_transactions[i].streamRLP( txrlp ); - transactionsMap.insert( std::make_pair( k.out(), txrlp.out() ) ); + dev::bytes txOutput = m_transactions[i].toBytes(); + if ( EIP1559TransactionsPatch::isEnabledInWorkingBlock() && + m_transactions[i].txType() != dev::eth::TransactionType::Legacy ) { + RLPStream s; + s.append( txOutput ); + txOutput = s.out(); + } + transactionsMap.insert( std::make_pair( k.out(), txOutput ) ); - txs.appendRaw( txrlp.out() ); + txs.appendRaw( txOutput ); } txs.swapOut( m_currentTxs ); @@ -1022,7 +1035,8 @@ void Block::commitToSeal( // Apply rewards last of all. assert( _bc.sealEngine() ); - applyRewards( uncleBlockHeaders, _bc.sealEngine()->blockReward( m_currentBlock.number() ) ); + applyRewards( uncleBlockHeaders, + _bc.sealEngine()->blockReward( previousInfo().timestamp(), m_currentBlock.number() ) ); // Commit any and all changes to the trie that are in the cache, then update the state root // accordingly. diff --git a/libethereum/Block.h b/libethereum/Block.h index 204224248..4164b73e9 100644 --- a/libethereum/Block.h +++ b/libethereum/Block.h @@ -299,6 +299,7 @@ class Block { /// Get the header information on the present block. BlockHeader const& info() const { return m_currentBlock; } + BlockHeader const& previousInfo() const { return m_previousBlock; } void startReadState(); diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 677d19b48..e2339685e 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -64,6 +65,8 @@ using skale::BaseState; using skale::State; using namespace skale::error; +extern bytesConstRef bytesRefFromTransactionRlp( const RLP& _rlp ); + #define ETH_TIMED_IMPORTS 1 namespace { @@ -173,24 +176,6 @@ unsigned c_maxCacheSize = 1024 * 1024 * 64; /// Min size, below which we don't bother flushing it. unsigned c_minCacheSize = 1024 * 1024 * 32; -bool hasPotentialInvalidTransactionsInBlock( BlockNumber _bn, const BlockChain& _bc ) { - if ( _bn == 0 ) - return false; - - if ( SkipInvalidTransactionsPatch::getActivationTimestamp() == 0 ) - return true; - - if ( _bn == PendingBlock ) - return !SkipInvalidTransactionsPatch::isEnabled(); - - if ( _bn == LatestBlock ) - _bn = _bc.number(); - - time_t prev_ts = _bc.info( _bc.numberHash( _bn - 1 ) ).timestamp(); - - return prev_ts < SkipInvalidTransactionsPatch::getActivationTimestamp(); -} - string BlockChain::getChainDirName( const ChainParams& _cp ) { return toHex( BlockHeader( _cp.genesisBlock() ).hash().ref().cropped( 0, 4 ) ); } @@ -370,7 +355,8 @@ std::pair< h256, unsigned > BlockChain::transactionLocation( h256 const& _transa auto blockNumber = this->number( ta.blockHash ); - if ( !hasPotentialInvalidTransactionsInBlock( blockNumber, *this ) ) + if ( !SkipInvalidTransactionsPatch::hasPotentialInvalidTransactionsInBlock( + blockNumber, *this ) ) return std::make_pair( ta.blockHash, ta.index ); // rest is for blocks with possibility of invalid transactions @@ -777,8 +763,10 @@ size_t BlockChain::prepareDbDataAndReturnSize( VerifiedBlockRef const& _block, for ( RLP::iterator it = txns_rlp.begin(); it != txns_rlp.end(); ++it ) { MICROPROFILE_SCOPEI( "insertBlockAndExtras", "for2", MP_HONEYDEW ); - extrasWriteBatch.insert( toSlice( sha3( ( *it ).data() ), ExtraTransactionAddress ), + auto txBytes = bytesRefFromTransactionRlp( *it ); + extrasWriteBatch.insert( toSlice( sha3( txBytes ), ExtraTransactionAddress ), ( db::Slice ) dev::ref( ta.rlp() ) ); + ++ta.index; } } @@ -1101,7 +1089,7 @@ void BlockChain::clearBlockBlooms( unsigned _begin, unsigned _end ) { } void BlockChain::rescue( State const& /*_state*/ ) { - clog( VerbosityInfo, "BlockChain" ) << "Rescuing database..." << endl; + clog( VerbosityInfo, "BlockChain" ) << "Rescuing database..."; throw std::logic_error( "Rescueing is not implemented" ); unsigned u = 1; @@ -1116,8 +1104,7 @@ void BlockChain::rescue( State const& /*_state*/ ) { } } unsigned l = u / 2; - clog( VerbosityInfo, "BlockChain" ) - << cc::debug( "Finding last likely block number..." ) << endl; + clog( VerbosityInfo, "BlockChain" ) << cc::debug( "Finding last likely block number..." ); while ( u - l > 1 ) { unsigned m = ( u + l ) / 2; clog( VerbosityInfo, "BlockChain" ) << " " << m << flush; @@ -1126,7 +1113,7 @@ void BlockChain::rescue( State const& /*_state*/ ) { else u = m; } - clog( VerbosityInfo, "BlockChain" ) << " lowest is " << l << endl; + clog( VerbosityInfo, "BlockChain" ) << " lowest is " << l; for ( ; l > 0; --l ) { h256 h = numberHash( l ); clog( VerbosityInfo, "BlockChain" ) @@ -1145,7 +1132,7 @@ void BlockChain::rescue( State const& /*_state*/ ) { } catch ( ... ) { } } - clog( VerbosityInfo, "BlockChain" ) << "OK." << endl; + clog( VerbosityInfo, "BlockChain" ) << "OK."; rewind( l ); } @@ -1747,12 +1734,18 @@ VerifiedBlockRef BlockChain::verifyBlock( bytesConstRef _block, ( ImportRequirements::TransactionBasic | ImportRequirements::TransactionSignatures ) ) { MICROPROFILE_SCOPEI( "BlockChain", "check txns", MP_ROSYBROWN ); for ( RLP const& tr : r[1] ) { - bytesConstRef d = tr.data(); + bytesConstRef d = bytesRefFromTransactionRlp( tr ); try { - Transaction t( d, ( _ir & ImportRequirements::TransactionSignatures ) ? - CheckTransaction::Everything : - CheckTransaction::None ); - m_sealEngine->verifyTransaction( _ir, t, h, 0 ); // the gasUsed vs + Transaction t( d, + ( _ir & ImportRequirements::TransactionSignatures ) ? + CheckTransaction::Everything : + CheckTransaction::None, + false, + EIP1559TransactionsPatch::isEnabledWhen( + this->info( numberHash( h.number() - 1 ) ).timestamp() ) ); + Ethash::verifyTransaction( chainParams(), _ir, t, + this->info( numberHash( h.number() - 1 ) ).timestamp(), h, + 0 ); // the gasUsed vs // blockGasLimit is checked // later in enact function res.transactions.push_back( t ); @@ -1776,3 +1769,18 @@ unsigned BlockChain::chainStartBlockNumber() const { auto const value = m_extrasDB->lookup( c_sliceChainStart ); return value.empty() ? 0 : number( h256( value, h256::FromBinary ) ); } + +bool BlockChain::isPatchTimestampActiveInBlockNumber( time_t _ts, BlockNumber _bn ) const { + if ( _bn == 0 || _ts == 0 ) + return false; + + if ( _bn == LatestBlock ) + _bn = number(); + + if ( _bn == PendingBlock ) + _bn = number() + 1; + + time_t prev_ts = this->info( this->numberHash( _bn - 1 ) ).timestamp(); + + return prev_ts >= _ts; +} diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 593475fb1..7a7ae5f5a 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -231,8 +231,8 @@ class BlockChain { auto b = block( _hash ); RLP rlp( b ); h256s ret; - for ( auto t : rlp[1] ) - ret.push_back( sha3( t.data() ) ); + for ( auto d : rlp[1] ) + ret.push_back( sha3( bytesRefFromTransactionRlp( d ) ) ); return ret; } TransactionHashes transactionHashes() const { return transactionHashes( currentHash() ); } @@ -319,7 +319,7 @@ class BlockChain { /// none given) & index. Thread-safe. bytes transaction( h256 const& _blockHash, unsigned _i ) const { bytes b = block( _blockHash ); - return RLP( b )[1][_i].data().toBytes(); + return bytesRefFromTransactionRlp( RLP( b )[1][_i] ).toBytes(); } bytes transaction( unsigned _i ) const { return transaction( currentHash(), _i ); } @@ -328,7 +328,7 @@ class BlockChain { bytes b = block( _blockHash ); std::vector< bytes > ret; for ( auto const& i : RLP( b )[1] ) - ret.push_back( i.data().toBytes() ); + ret.push_back( bytesRefFromTransactionRlp( i ).toBytes() ); return ret; } std::vector< bytes > transactions() const { return transactions( currentHash() ); } @@ -453,6 +453,10 @@ class BlockChain { return 0; } + // simple thing to compare _bn-1's timestamp with ts + // maybe need cahing for faster operation + bool isPatchTimestampActiveInBlockNumber( time_t _ts, BlockNumber _bn ) const; + private: static h256 chunkId( unsigned _level, unsigned _index ) { return h256( _index * 0xff + _level ); diff --git a/libethereum/CMakeLists.txt b/libethereum/CMakeLists.txt index 0a79f6baa..6b92ea48a 100644 --- a/libethereum/CMakeLists.txt +++ b/libethereum/CMakeLists.txt @@ -1,6 +1,7 @@ file( GLOB sources "*.cpp" "*.h" ) -add_library( ethereum ${sources} InstanceMonitor.cpp InstanceMonitor.h) +add_library( ethereum ${sources} InstanceMonitor.cpp InstanceMonitor.h + SchainPatchEnum.h) target_compile_options( ethereum PRIVATE -Wno-error=deprecated-copy -Wno-error=unused-result -Wno-error=unused-parameter -Wno-error=unused-variable -Wno-error=maybe-uninitialized diff --git a/libethereum/ChainParams.cpp b/libethereum/ChainParams.cpp index 23134a877..4b2be0789 100644 --- a/libethereum/ChainParams.cpp +++ b/libethereum/ChainParams.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -38,11 +39,12 @@ #include #include "Account.h" -#include "GenesisInfo.h" #include "ValidationSchemes.h" #include +#include + using namespace std; using namespace dev; using namespace eth; @@ -104,7 +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 ); @@ -214,40 +217,40 @@ void ChainParams::processSkaleConfigItems( ChainParams& cp, json_spirit::mObject if ( cp.rotateAfterBlock_ < 0 ) cp.rotateAfterBlock_ = 0; - string ecdsaKeyName; + bool testSignatures = false; try { - ecdsaKeyName = infoObj.at( "ecdsaKeyName" ).get_str(); + testSignatures = infoObj.at( "testSignatures" ).get_bool(); } catch ( ... ) { } + string ecdsaKeyName; array< string, 4 > BLSPublicKeys; array< string, 4 > commonBLSPublicKeys; + if ( !testSignatures ) { + ecdsaKeyName = infoObj.at( "ecdsaKeyName" ).get_str(); - try { js::mObject ima = infoObj.at( "wallets" ).get_obj().at( "ima" ).get_obj(); - keyShareName = ima.at( "keyShareName" ).get_str(); - - t = ima.at( "t" ).get_int(); - - BLSPublicKeys[0] = ima["BLSPublicKey0"].get_str(); - BLSPublicKeys[1] = ima["BLSPublicKey1"].get_str(); - BLSPublicKeys[2] = ima["BLSPublicKey2"].get_str(); - BLSPublicKeys[3] = ima["BLSPublicKey3"].get_str(); - commonBLSPublicKeys[0] = ima["commonBLSPublicKey0"].get_str(); commonBLSPublicKeys[1] = ima["commonBLSPublicKey1"].get_str(); commonBLSPublicKeys[2] = ima["commonBLSPublicKey2"].get_str(); commonBLSPublicKeys[3] = ima["commonBLSPublicKey3"].get_str(); - } catch ( ... ) { - // all or nothing - if ( !keyShareName.empty() ) - throw; + + if ( !syncNode ) { + keyShareName = ima.at( "keyShareName" ).get_str(); + + t = ima.at( "t" ).get_int(); + + BLSPublicKeys[0] = ima["BLSPublicKey0"].get_str(); + BLSPublicKeys[1] = ima["BLSPublicKey1"].get_str(); + BLSPublicKeys[2] = ima["BLSPublicKey2"].get_str(); + BLSPublicKeys[3] = ima["BLSPublicKey3"].get_str(); + } } cp.nodeInfo = { nodeName, nodeID, ip, static_cast< uint16_t >( port ), ip6, static_cast< uint16_t >( port6 ), sgxServerUrl, ecdsaKeyName, keyShareName, BLSPublicKeys, - commonBLSPublicKeys, syncNode, archiveMode, syncFromCatchup }; + commonBLSPublicKeys, syncNode, archiveMode, syncFromCatchup, testSignatures }; auto sChainObj = skaleObj.at( "sChain" ).get_obj(); SChain s{}; @@ -303,50 +306,17 @@ void ChainParams::processSkaleConfigItems( ChainParams& cp, json_spirit::mObject if ( sChainObj.count( "multiTransactionMode" ) ) s.multiTransactionMode = sChainObj.at( "multiTransactionMode" ).get_bool(); - if ( sChainObj.count( "revertableFSPatchTimestamp" ) ) - s.revertableFSPatchTimestamp = sChainObj.at( "revertableFSPatchTimestamp" ).get_int64(); - - s.contractStoragePatchTimestamp = - sChainObj.count( "contractStoragePatchTimestamp" ) ? - sChainObj.at( "contractStoragePatchTimestamp" ).get_int64() : - 0; - - s.contractStorageZeroValuePatchTimestamp = - sChainObj.count( "contractStorageZeroValuePatchTimestamp" ) ? - sChainObj.at( "contractStorageZeroValuePatchTimestamp" ).get_int64() : - 0; - - s.verifyDaSigsPatchTimestamp = sChainObj.count( "verifyDaSigsPatchTimestamp" ) ? - sChainObj.at( "verifyDaSigsPatchTimestamp" ).get_int64() : - 0; - - s.storageDestructionPatchTimestamp = - sChainObj.count( "storageDestructionPatchTimestamp" ) ? - sChainObj.at( "storageDestructionPatchTimestamp" ).get_int64() : - 0; - - s.powCheckPatchTimestamp = sChainObj.count( "powCheckPatchTimestamp" ) ? - sChainObj.at( "powCheckPatchTimestamp" ).get_int64() : - 0; - - s.precompiledConfigPatchTimestamp = - sChainObj.count( "precompiledConfigPatchTimestamp" ) ? - sChainObj.at( "precompiledConfigPatchTimestamp" ).get_int64() : - 0; - - s.pushZeroPatchTimestamp = sChainObj.count( "pushZeroPatchTimestamp" ) ? - sChainObj.at( "pushZeroPatchTimestamp" ).get_int64() : - 0; - - s.skipInvalidTransactionsPatchTimestamp = - sChainObj.count( "skipInvalidTransactionsPatchTimestamp" ) ? - sChainObj.at( "skipInvalidTransactionsPatchTimestamp" ).get_int64() : - 0; - - s.correctForkInPowPatchTimestamp = - sChainObj.count( "correctForkInPowPatchTimestamp" ) ? - sChainObj.at( "correctForkInPowPatchTimestamp" ).get_int64() : - 0; + // extract all "*PatchTimestamp" records + for ( const auto& it : sChainObj ) { + const string& key = it.first; + if ( boost::algorithm::ends_with( key, "PatchTimestamp" ) ) { + string patchName = boost::algorithm::erase_last_copy( key, "Timestamp" ); + patchName[0] = toupper( patchName[0] ); + SchainPatchEnum patchEnum = getEnumForPatchName( patchName ); + s._patchTimestamps[static_cast< size_t >( patchEnum )] = + it.second.get_int64(); // time_t is signed + } // if + } // for if ( sChainObj.count( "nodeGroups" ) ) { vector< NodeGroup > nodeGroups; diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index a2bcb2958..faf04ec7d 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -52,20 +52,11 @@ #include #endif +#include +#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include #include -#include #include #include @@ -163,21 +154,6 @@ Client::Client( ChainParams const& _params, int _networkID, }; init( _forceAction, _networkID ); - - // Set timestamps for patches - TotalStorageUsedPatch::g_client = this; - ContractStorageLimitPatch::setTimestamp( chainParams().sChain.contractStoragePatchTimestamp ); - ContractStorageZeroValuePatch::setTimestamp( - chainParams().sChain.contractStorageZeroValuePatchTimestamp ); - VerifyDaSigsPatch::setTimestamp( chainParams().sChain.verifyDaSigsPatchTimestamp ); - RevertableFSPatch::setTimestamp( chainParams().sChain.revertableFSPatchTimestamp ); - StorageDestructionPatch::setTimestamp( chainParams().sChain.storageDestructionPatchTimestamp ); - POWCheckPatch::setTimestamp( chainParams().sChain.powCheckPatchTimestamp ); - PushZeroPatch::setTimestamp( chainParams().sChain.pushZeroPatchTimestamp ); - SkipInvalidTransactionsPatch::setTimestamp( - this->chainParams().sChain.skipInvalidTransactionsPatchTimestamp ); - PrecompiledConfigPatch::setTimestamp( chainParams().sChain.precompiledConfigPatchTimestamp ); - CorrectForkInPowPatch::setTimestamp( chainParams().sChain.correctForkInPowPatchTimestamp ); } @@ -337,13 +313,12 @@ void Client::init( WithExisting _forceAction, u256 _networkId ) { m_snapshotAgentInited = true; } + SchainPatch::init( chainParams() ); + SchainPatch::useLatestBlockTimestamp( blockChain().info().timestamp() ); + TotalStorageUsedPatch::init( this ); // 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 ); @@ -613,9 +588,7 @@ 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(); + SchainPatch::useLatestBlockTimestamp( blockChain().info().timestamp() ); if ( !UnsafeRegion::isActive() ) { LOG( m_loggerDetail ) << "Total unsafe time so far = " @@ -666,7 +639,7 @@ size_t Client::syncTransactions( // HACK remove block verification and put it directly in blockchain!! // TODO remove block verification and put it directly in blockchain!! while ( m_working.isSealed() ) { - cnote << "m_working.isSealed. sleeping" << endl; + cnote << "m_working.isSealed. sleeping"; usleep( 1000 ); } @@ -677,18 +650,6 @@ size_t Client::syncTransactions( TransactionReceipts newPendingReceipts; unsigned goodReceipts; - ContractStorageLimitPatch::lastBlockTimestamp = blockChain().info().timestamp(); - ContractStorageZeroValuePatch::lastBlockTimestamp = blockChain().info().timestamp(); - RevertableFSPatch::lastBlockTimestamp = blockChain().info().timestamp(); - StorageDestructionPatch::lastBlockTimestamp = blockChain().info().timestamp(); - POWCheckPatch::lastBlockTimestamp = blockChain().info().timestamp(); - 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() ); @@ -1131,19 +1092,6 @@ Block Client::blockByNumber( BlockNumber _h ) const { } #endif -Block Client::latestBlock() const { - // TODO Why it returns not-filled block??! (see Block ctor) - try { - DEV_GUARDED( m_blockImportMutex ) { return Block( bc(), bc().currentHash(), m_state ); } - assert( false ); - return Block( bc() ); - } catch ( Exception& ex ) { - ex << errinfo_block( bc().block( bc().currentHash() ) ); - onBadBlock( ex ); - return Block( bc() ); - } -} - void Client::flushTransactions() { doWork(); } @@ -1221,14 +1169,15 @@ h256 Client::importTransaction( Transaction const& _t ) { state = this->state().createStateReadOnlyCopy(); gasBidPrice = this->gasBidPrice(); - // We need to check external gas under mutex to be sure about current block bumber + // We need to check external gas under mutex to be sure about current block number // correctness - const_cast< Transaction& >( _t ).checkOutExternalGas( chainParams(), number() ); + const_cast< Transaction& >( _t ).checkOutExternalGas( + chainParams(), bc().info().timestamp(), number(), false ); } - Executive::verifyTransaction( _t, + Executive::verifyTransaction( _t, bc().info().timestamp(), bc().number() ? this->blockInfo( bc().currentHash() ) : bc().genesis(), state, - *bc().sealEngine(), 0, gasBidPrice, chainParams().sChain.multiTransactionMode ); + chainParams(), 0, gasBidPrice, chainParams().sChain.multiTransactionMode ); ImportResult res; if ( chainParams().sChain.multiTransactionMode && state.getNonce( _t.sender() ) < _t.nonce() && @@ -1298,7 +1247,7 @@ ExecutionResult Client::call( Address const& _from, u256 _value, Address _dest, } #endif - Block temp = latestBlock(); + Block temp = preSeal(); // TODO there can be race conditions between prev and next line! State readStateForLock = temp.mutableState().createStateReadOnlyCopy(); @@ -1399,7 +1348,7 @@ Json::Value Client::traceBlock( BlockNumber _blockNumber, Json::Value const& _js Transaction tx = transactions.at( k ); auto hashString = toHexPrefixed( tx.sha3() ); transactionLog["txHash"] = hashString; - tx.checkOutExternalGas( chainParams(), _blockNumber ); + tx.checkOutExternalGas( chainParams(), bc().info().timestamp(), number(), false ); auto tracer = std::make_shared< AlethStandardTrace >( tx, historicBlock.author(), traceOptions ); auto executionResult = diff --git a/libethereum/Client.h b/libethereum/Client.h index 4f7b8ea3d..468aec177 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -142,6 +142,10 @@ class Client : public ClientBase, protected Worker { /// Blocks until all pending transactions have been processed. void flushTransactions() override; + using ClientBase::blockDetails; + using ClientBase::blockInfo; // for another overload + using ClientBase::uncleHashes; + /// Retrieve pending transactions Transactions pending() const override; @@ -153,7 +157,9 @@ class Client : public ClientBase, protected Worker { /// Get the remaining gas limit in this block. u256 gasLimitRemaining() const override { return m_postSeal.gasLimitRemaining(); } /// Get the gas bid price - u256 gasBidPrice() const override { return m_gp->bid(); } + u256 gasBidPrice( unsigned _blockNumber = dev::eth::LatestBlock ) const override { + return m_gp->bid( _blockNumber ); + } // [PRIVATE API - only relevant for base clients, not available in general] /// Get the block. @@ -264,8 +270,6 @@ class Client : public ClientBase, protected Worker { /// Queues a function to be executed in the main thread (that owns the blockchain, etc). void executeInMainThread( std::function< void() > const& _function ); - Block latestBlock() const; - /// should be called after the constructor of the most derived class finishes. void startWorking() { assert( m_skaleHost ); diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 6b6d530b4..04b4302a8 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -23,7 +23,7 @@ */ #include "ClientBase.h" -#include +#include #include #include @@ -80,8 +80,8 @@ void ClientWatch::append_changes( const LocalisedLogEntry& entry ) { } std::pair< bool, ExecutionResult > ClientBase::estimateGasStep( int64_t _gas, Block& _latestBlock, - Address const& _from, Address const& _destination, u256 const& _value, u256 const& _gasPrice, - bytes const& _data ) { + Block& _pendingBlock, Address const& _from, Address const& _destination, u256 const& _value, + u256 const& _gasPrice, bytes const& _data ) { u256 nonce = _latestBlock.transactionsFrom( _from ); Transaction t; if ( _destination ) @@ -91,12 +91,13 @@ std::pair< bool, ExecutionResult > ClientBase::estimateGasStep( int64_t _gas, Bl t.forceSender( _from ); t.forceChainId( chainId() ); t.ignoreExternalGas(); - EnvInfo const env( _latestBlock.info(), bc().lastBlockHashes(), 0, _gas ); + EnvInfo const env( _pendingBlock.info(), bc().lastBlockHashes(), + _pendingBlock.previousInfo().timestamp(), 0, _gas ); // Make a copy of state!! It will be deleted after step! State tempState = _latestBlock.mutableState(); tempState.addBalance( _from, ( u256 )( t.gas() * t.gasPrice() + t.value() ) ); ExecutionResult executionResult = - tempState.execute( env, *bc().sealEngine(), t, Permanence::Reverted ).first; + tempState.execute( env, bc().chainParams(), t, Permanence::Reverted ).first; if ( executionResult.excepted == TransactionException::OutOfGas || executionResult.excepted == TransactionException::OutOfGasBase || executionResult.excepted == TransactionException::OutOfGasIntrinsic || @@ -116,15 +117,19 @@ 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 = - 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 >(); + int64_t lowerBound; + if ( CorrectForkInPowPatch::isEnabledInWorkingBlock() ) + lowerBound = Transaction::baseGasRequired( !_dest, &_data, + bc().sealEngine()->chainParams().makeEvmSchedule( + bc().info().timestamp(), bc().number() ) ); + else + lowerBound = Transaction::baseGasRequired( !_dest, &_data, EVMSchedule() ); + + Block latest = latestBlock(); + Block pending = preSeal(); + + if ( upperBound > pending.info().gasLimit() ) { + upperBound = pending.info().gasLimit().convert_to< int64_t >(); } u256 gasPrice = _gasPrice == Invalid256 ? gasBidPrice() : _gasPrice; @@ -135,19 +140,20 @@ std::pair< u256, ExecutionResult > ClientBase::estimateGas( Address const& _from // If not run binary search to find optimal gas limit. auto estimatedStep = - estimateGasStep( upperBound, bk, _from, _dest, _value, gasPrice, _data ); + estimateGasStep( upperBound, latest, pending, _from, _dest, _value, gasPrice, _data ); if ( estimatedStep.first ) { auto executionResult = estimatedStep.second; auto gasUsed = std::max( executionResult.gasUsed.convert_to< int64_t >(), lowerBound ); - estimatedStep = estimateGasStep( gasUsed, bk, _from, _dest, _value, gasPrice, _data ); + estimatedStep = + estimateGasStep( gasUsed, latest, pending, _from, _dest, _value, gasPrice, _data ); if ( estimatedStep.first ) { return make_pair( gasUsed, executionResult ); } while ( lowerBound + 1 < upperBound ) { int64_t middle = ( lowerBound + upperBound ) / 2; - estimatedStep = - estimateGasStep( middle, bk, _from, _dest, _value, gasPrice, _data ); + estimatedStep = estimateGasStep( + middle, latest, pending, _from, _dest, _value, gasPrice, _data ); if ( estimatedStep.first ) { upperBound = middle; } else { @@ -160,7 +166,8 @@ std::pair< u256, ExecutionResult > ClientBase::estimateGas( Address const& _from } return make_pair( upperBound, - estimateGasStep( upperBound, bk, _from, _dest, _value, gasPrice, _data ).second ); + estimateGasStep( upperBound, latest, pending, _from, _dest, _value, gasPrice, _data ) + .second ); } catch ( ... ) { // TODO: Some sort of notification of failure. return make_pair( u256(), ExecutionResult() ); @@ -212,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 > ( uint64_t ) 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(); @@ -343,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; @@ -382,7 +380,10 @@ BlockDetails ClientBase::blockDetails( h256 _hash ) const { Transaction ClientBase::transaction( h256 _transactionHash ) const { // allow invalid! - return Transaction( bc().transaction( _transactionHash ), CheckTransaction::Cheap, true ); + auto tl = bc().transactionLocation( _transactionHash ); + return Transaction( bc().transaction( _transactionHash ), CheckTransaction::Cheap, true, + EIP1559TransactionsPatch::isEnabledWhen( + blockInfo( numberFromHash( tl.first ) - 1 ).timestamp() ) ); } LocalisedTransaction ClientBase::localisedTransaction( h256 const& _transactionHash ) const { @@ -395,15 +396,18 @@ Transaction ClientBase::transaction( h256 _blockHash, unsigned _i ) const { RLP b( bl ); if ( _i < b[1].itemCount() ) // allow invalid - return Transaction( b[1][_i].data(), CheckTransaction::Cheap, true ); + return Transaction( b[1][_i].data(), CheckTransaction::Cheap, true, + EIP1559TransactionsPatch::isEnabledWhen( + blockInfo( numberFromHash( _blockHash ) - 1 ).timestamp() ) ); else return Transaction(); } LocalisedTransaction ClientBase::localisedTransaction( h256 const& _blockHash, unsigned _i ) const { // allow invalid - Transaction t = - Transaction( bc().transaction( _blockHash, _i ), CheckTransaction::Cheap, true ); + Transaction t = Transaction( bc().transaction( _blockHash, _i ), CheckTransaction::Cheap, true, + EIP1559TransactionsPatch::isEnabledWhen( + blockInfo( numberFromHash( _blockHash ) - 1 ).timestamp() ) ); return LocalisedTransaction( t, _blockHash, _i, numberFromHash( _blockHash ) ); } @@ -416,7 +420,9 @@ LocalisedTransactionReceipt ClientBase::localisedTransactionReceipt( std::pair< h256, unsigned > tl = bc().transactionLocation( _transactionHash ); // allow invalid Transaction t = - Transaction( bc().transaction( tl.first, tl.second ), CheckTransaction::Cheap, true ); + Transaction( bc().transaction( tl.first, tl.second ), CheckTransaction::Cheap, true, + EIP1559TransactionsPatch::isEnabledWhen( + blockInfo( numberFromHash( tl.first ) - 1 ).timestamp() ) ); TransactionReceipt tr = bc().transactionReceipt( tl.first, tl.second ); u256 gasUsed = tr.cumulativeGasUsed(); if ( tl.second > 0 ) @@ -435,7 +441,8 @@ LocalisedTransactionReceipt ClientBase::localisedTransactionReceipt( // return LocalisedTransactionReceipt( tr, t.sha3(), tl.first, numberFromHash( tl.first ), tl.second, t.isInvalid() ? dev::Address( 0 ) : t.from(), - t.isInvalid() ? dev::Address( 0 ) : t.to(), gasUsed, contractAddress ); + t.isInvalid() ? dev::Address( 0 ) : t.to(), gasUsed, contractAddress, int( t.txType() ), + t.isInvalid() ? 0 : t.gasPrice() ); } pair< h256, unsigned > ClientBase::transactionLocation( h256 const& _transactionHash ) const { @@ -446,8 +453,12 @@ Transactions ClientBase::transactions( h256 _blockHash ) const { auto bl = bc().block( _blockHash ); RLP b( bl ); Transactions res; - for ( unsigned i = 0; i < b[1].itemCount(); i++ ) - res.emplace_back( b[1][i].data(), CheckTransaction::Cheap, true ); + for ( unsigned i = 0; i < b[1].itemCount(); i++ ) { + auto txRlp = b[1][i]; + res.emplace_back( bytesRefFromTransactionRlp( txRlp ), CheckTransaction::Cheap, true, + EIP1559TransactionsPatch::isEnabledWhen( + blockInfo( numberFromHash( _blockHash ) - 1 ).timestamp() ) ); + } return res; } @@ -499,6 +510,10 @@ BlockDetails ClientBase::pendingDetails() const { pm.parentHash(), h256s{}, postSeal().blockData().size() ); } +EVMSchedule ClientBase::evmSchedule() const { + return sealEngine()->evmSchedule( bc().info().timestamp(), pendingInfo().number() ); +} + u256 ClientBase::gasLimitRemaining() const { return postSeal().gasLimitRemaining(); } diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index ab4b7a812..96f769b5f 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -92,7 +92,7 @@ class ClientBase : public Interface { /// @param _callback Optional callback function for progress reporting std::pair< u256, ExecutionResult > estimateGas( Address const& _from, u256 _value, Address _dest, bytes const& _data, int64_t _maxGas, u256 _gasPrice, - GasEstimationCallback const& _callback ) override; + GasEstimationCallback const& _callback = GasEstimationCallback() ) override; u256 balanceAt( Address _a ) const override; u256 countAt( Address _a ) const override; @@ -114,9 +114,14 @@ 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; + using Interface::blockInfo; // for another overload + using Interface::transactionHashes; + using Interface::uncle; + using Interface::uncleHashes; + h256 hashFromNumber( BlockNumber _number ) const override; BlockNumber numberFromHash( h256 _blockHash ) const override; int compareBlockHashes( h256 _h1, h256 _h2 ) const override; @@ -147,20 +152,19 @@ class ClientBase : public Interface { } return transactionCount( hashFromNumber( _block ) ); } + using Interface::uncleCount; unsigned uncleCount( h256 _blockHash ) const override; unsigned number() const override; h256s pendingHashes() const override; BlockHeader pendingInfo() const override; BlockDetails pendingDetails() const override; - EVMSchedule evmSchedule() const override { - return sealEngine()->evmSchedule( pendingInfo().number() ); - } + EVMSchedule evmSchedule() const override; ImportResult injectBlock( bytes const& _block ) override; u256 gasLimitRemaining() const override; - u256 gasBidPrice() const override { return DefaultGasPrice; } + u256 gasBidPrice( unsigned = dev::eth::LatestBlock ) const override { return DefaultGasPrice; } /// Get the block author Address author() const override; @@ -217,7 +221,7 @@ class ClientBase : public Interface { private: std::pair< bool, ExecutionResult > estimateGasStep( int64_t _gas, Block& _latestBlock, - Address const& _from, Address const& _destination, u256 const& _value, + Block& _pendingBlock, Address const& _from, Address const& _destination, u256 const& _value, u256 const& _gasPrice, bytes const& _data ); }; diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index d3e93b6b8..242245ba7 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -26,7 +26,9 @@ #include #include #include +#include #include +#include #include #include @@ -164,19 +166,21 @@ string StandardTrace::json( bool _styled ) const { Executive::Executive( Block& _s, BlockChain const& _bc, const u256& _gasPrice, unsigned _level, bool _readOnly ) : m_s( _s.mutableState() ), - m_envInfo( _s.info(), _bc.lastBlockHashes(), 0, _bc.chainID() ), + m_envInfo( + _s.info(), _bc.lastBlockHashes(), _s.previousInfo().timestamp(), 0, _bc.chainID() ), m_depth( _level ), m_readOnly( _readOnly ), - m_sealEngine( *_bc.sealEngine() ), + m_chainParams( _bc.chainParams() ), m_systemGasPrice( _gasPrice ) {} Executive::Executive( Block& _s, LastBlockHashesFace const& _lh, const u256& _gasPrice, unsigned _level, bool _readOnly ) : m_s( _s.mutableState() ), - m_envInfo( _s.info(), _lh, 0, _s.sealEngine()->chainParams().chainID ), + m_envInfo( _s.info(), _lh, _s.previousInfo().timestamp(), 0, + _s.sealEngine()->chainParams().chainID ), m_depth( _level ), m_readOnly( _readOnly ), - m_sealEngine( *_s.sealEngine() ), + m_chainParams( _s.sealEngine()->chainParams() ), m_systemGasPrice( _gasPrice ) {} u256 Executive::gasUsed() const { @@ -188,9 +192,10 @@ void Executive::accrueSubState( SubState& _parentContext ) { _parentContext += m_ext->sub; } -void Executive::verifyTransaction( Transaction const& _transaction, BlockHeader const& _blockHeader, - const State& _state, const SealEngineFace& _sealEngine, u256 const& _gasUsed, - const u256& _gasPrice, const bool _allowFuture ) { +void Executive::verifyTransaction( Transaction const& _transaction, time_t _committedBlockTimestamp, + BlockHeader const& _blockHeader, const State& _state, + const eth::ChainOperationParams& _chainParams, u256 const& _gasUsed, const u256& _gasPrice, + const bool _allowFuture ) { MICROPROFILE_SCOPEI( "Executive", "verifyTransaction", MP_GAINSBORO ); if ( !_transaction.hasExternalGas() && _transaction.gasPrice() < _gasPrice ) { @@ -199,8 +204,8 @@ void Executive::verifyTransaction( Transaction const& _transaction, BlockHeader static_cast< bigint >( _transaction.gasPrice() ) ) ); } - _sealEngine.verifyTransaction( - ImportRequirements::Everything, _transaction, _blockHeader, _gasUsed ); + Ethash::verifyTransaction( _chainParams, ImportRequirements::Everything, _transaction, + _committedBlockTimestamp, _blockHeader, _gasUsed ); if ( !_transaction.hasZeroSignature() ) { // skip nonce check for calls @@ -244,11 +249,12 @@ void Executive::verifyTransaction( Transaction const& _transaction, BlockHeader void Executive::initialize( Transaction const& _transaction ) { MICROPROFILE_SCOPEI( "Executive", "initialize", MP_GAINSBORO ); m_t = _transaction; - m_baseGasRequired = m_t.baseGasRequired( m_sealEngine.evmSchedule( m_envInfo.number() ) ); + m_baseGasRequired = m_t.baseGasRequired( + m_chainParams.makeEvmSchedule( m_envInfo.committedBlockTimestamp(), m_envInfo.number() ) ); try { - verifyTransaction( _transaction, m_envInfo.header(), m_s, m_sealEngine, m_envInfo.gasUsed(), - m_systemGasPrice ); + verifyTransaction( _transaction, m_envInfo.committedBlockTimestamp(), m_envInfo.header(), + m_s, m_chainParams, m_envInfo.gasUsed(), m_systemGasPrice ); } catch ( Exception const& ex ) { m_excepted = toTransactionException( ex ); throw; @@ -293,7 +299,7 @@ bool Executive::call( CallParameters const& _p, u256 const& _gasPrice, Address c // for the transaction. // Increment associated nonce for sender. if ( _p.senderAddress != MaxAddress || - m_envInfo.number() < m_sealEngine.chainParams().constantinopleForkBlock ) { // EIP86 + m_envInfo.number() < m_chainParams.constantinopleForkBlock ) { // EIP86 MICROPROFILE_SCOPEI( "Executive", "call-incNonce", MP_SEAGREEN ); m_s.incNonce( _p.senderAddress ); } @@ -301,11 +307,11 @@ bool Executive::call( CallParameters const& _p, u256 const& _gasPrice, Address c m_savepoint = m_s.savepoint(); - if ( m_sealEngine.isPrecompiled( _p.codeAddress, m_envInfo.number() ) && - m_sealEngine.precompiledExecutionAllowedFrom( + if ( m_chainParams.isPrecompiled( _p.codeAddress, m_envInfo.number() ) && + m_chainParams.precompiledExecutionAllowedFrom( _p.codeAddress, _p.senderAddress, m_readOnly ) ) { MICROPROFILE_SCOPEI( "Executive", "call-precompiled", MP_CYAN ); - bigint g = m_sealEngine.costOfPrecompiled( _p.codeAddress, _p.data, m_envInfo.number() ); + bigint g = m_chainParams.costOfPrecompiled( _p.codeAddress, _p.data, m_envInfo.number() ); if ( _p.gas < g ) { m_excepted = TransactionException::OutOfGasBase; // Bail from exception. @@ -316,7 +322,7 @@ bool Executive::call( CallParameters const& _p, u256 const& _gasPrice, Address c // https://github.com/ethereum/go-ethereum/pull/3341/files#diff-2433aa143ee4772026454b8abd76b9dd // We mark the account as touched here, so that is can be removed among other touched // empty accounts (after tx finalization) - if ( m_envInfo.number() >= m_sealEngine.chainParams().EIP158ForkBlock ) + if ( m_envInfo.number() >= m_chainParams.EIP158ForkBlock ) m_s.addBalance( _p.codeAddress, 0 ); return true; // true actually means "all finished - nothing more to be done regarding @@ -328,7 +334,7 @@ bool Executive::call( CallParameters const& _p, u256 const& _gasPrice, Address c // dev::eth::g_state = m_s.delegateWrite(); dev::eth::g_overlayFS = m_s.fs(); tie( success, output ) = - m_sealEngine.executePrecompiled( _p.codeAddress, _p.data, m_envInfo.number() ); + m_chainParams.executePrecompiled( _p.codeAddress, _p.data, m_envInfo.number() ); // m_s = dev::eth::g_state.delegateWrite(); size_t outputSize = output.size(); m_output = owning_bytes_ref{ std::move( output ), 0, outputSize }; @@ -346,7 +352,7 @@ bool Executive::call( CallParameters const& _p, u256 const& _gasPrice, Address c h256 codeHash = m_s.codeHash( _p.codeAddress ); // Contract will be executed with the version stored in account auto const version = m_s.version( _p.codeAddress ); - m_ext = make_shared< ExtVM >( m_s, m_envInfo, m_sealEngine, _p.receiveAddress, + m_ext = make_shared< ExtVM >( m_s, m_envInfo, m_chainParams, _p.receiveAddress, _p.senderAddress, _origin, _p.apparentValue, _gasPrice, _p.data, &c, codeHash, version, m_depth, false, _p.staticCall, m_readOnly ); } @@ -388,7 +394,7 @@ bool Executive::executeCreate( Address const& _sender, u256 const& _endowment, u256 const& _gasPrice, u256 const& _gas, bytesConstRef _init, Address const& _origin, u256 const& _version ) { if ( _sender != MaxAddress || - m_envInfo.number() < m_sealEngine.chainParams().experimentalForkBlock ) // EIP86 + m_envInfo.number() < m_chainParams.experimentalForkBlock ) // EIP86 m_s.incNonce( _sender ); m_savepoint = m_s.savepoint(); @@ -415,7 +421,7 @@ bool Executive::executeCreate( Address const& _sender, u256 const& _endowment, m_s.transferBalance( _sender, m_newAddress, _endowment ); u256 newNonce = m_s.requireAccountStartNonce(); - if ( m_envInfo.number() >= m_sealEngine.chainParams().EIP158ForkBlock ) + if ( m_envInfo.number() >= m_chainParams.EIP158ForkBlock ) newNonce += 1; m_s.setNonce( m_newAddress, newNonce ); @@ -423,7 +429,7 @@ bool Executive::executeCreate( Address const& _sender, u256 const& _endowment, // Schedule _init execution if not empty. if ( !_init.empty() ) - m_ext = make_shared< ExtVM >( m_s, m_envInfo, m_sealEngine, m_newAddress, _sender, _origin, + m_ext = make_shared< ExtVM >( m_s, m_envInfo, m_chainParams, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, sha3( _init ), _version, m_depth, true, false ); else @@ -463,11 +469,18 @@ bool Executive::go( OnOpFunc const& _onOp ) { // Create VM instance. Force Interpreter if tracing requested. auto vm = VMFactory::create(); if ( m_isCreation ) { - bytes in = isAddressWhitelistedCallData( m_ext->caller ); + // Checking whether deployment is allowed via ConfigController contract + bytes calldata; + if ( FlexibleDeploymentPatch::isEnabledWhen( + m_envInfo.committedBlockTimestamp() ) ) { + calldata = isDeploymentAllowedCallData( m_ext->origin, m_ext->caller ); + } else { + calldata = isAddressWhitelistedCallData( m_ext->caller ); + } unique_ptr< CallParameters > deploymentCallParams( new CallParameters( SystemAddress, c_configControllerContractAddress, c_configControllerContractAddress, 0, 0, m_gas, - bytesConstRef( in.data(), in.size() ), {} ) ); + bytesConstRef( calldata.data(), calldata.size() ), {} ) ); auto deploymentCallResult = m_ext->call( *deploymentCallParams ); auto deploymentCallOutput = dev::toHex( deploymentCallResult.output ); if ( !deploymentCallOutput.empty() && u256( deploymentCallOutput ) == 0 ) { diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 9bc8c0ec5..cf1b2715c 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -102,13 +102,13 @@ class StandardTrace { class Executive { public: /// Simple constructor; executive will operate on given state, with the given environment info. - Executive( skale::State& _s, EnvInfo const& _envInfo, SealEngineFace const& _sealEngine, - const u256& _gasPrice, unsigned _level = 0, bool _readOnly = true ) + Executive( skale::State& _s, EnvInfo const& _envInfo, ChainOperationParams const& _chainParams, + const u256& _gasPrice, unsigned _level, bool _readOnly = true ) : m_s( _s ), m_envInfo( _envInfo ), m_depth( _level ), m_readOnly( _readOnly ), - m_sealEngine( _sealEngine ), + m_chainParams( _chainParams ), m_systemGasPrice( _gasPrice ) {} /** Easiest constructor. @@ -202,9 +202,10 @@ class Executive { /// Revert all changes made to the state by this execution. void revert(); - static void verifyTransaction( Transaction const& _transaction, BlockHeader const& _blockHeader, - const skale::State& _state, const SealEngineFace& _sealEngine, u256 const& _gasUsed, - const u256& _gasPrice, const bool _allowFuture = false ); + static void verifyTransaction( Transaction const& _transaction, time_t _committedBlockTimestamp, + BlockHeader const& _blockHeader, const skale::State& _state, + const ChainOperationParams& _chainParams, u256 const& _gasUsed, const u256& _gasPrice, + const bool _allowFuture = false ); private: /// @returns false iff go() must be called (and thus a VM execution in required). @@ -235,7 +236,7 @@ class Executive { LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize(). u256 m_gasCost; - SealEngineFace const& m_sealEngine; + ChainOperationParams const& m_chainParams; u256 m_systemGasPrice; bool m_isCreation = false; diff --git a/libethereum/ExtVM.cpp b/libethereum/ExtVM.cpp index a20931378..453cb0ec0 100644 --- a/libethereum/ExtVM.cpp +++ b/libethereum/ExtVM.cpp @@ -127,7 +127,7 @@ evmc_status_code transactionExceptionToEvmcStatusCode( TransactionException ex ) CallResult ExtVM::call( CallParameters& _p ) { - Executive e{ m_s, envInfo(), m_sealEngine, 0, depth + 1, m_readOnly }; + Executive e{ m_s, envInfo(), m_chainParams, 0, depth + 1, m_readOnly }; if ( !e.call( _p, gasPrice, origin ) ) { go( depth, e, _p.onOp ); e.accrueSubState( sub ); @@ -151,7 +151,7 @@ void ExtVM::setStore( u256 _n, u256 _v ) { CreateResult ExtVM::create( u256 _endowment, u256& io_gas, bytesConstRef _code, Instruction _op, u256 _salt, OnOpFunc const& _onOp ) { - Executive e{ m_s, envInfo(), m_sealEngine, 0, depth + 1 }; + Executive e{ m_s, envInfo(), m_chainParams, 0, depth + 1 }; bool result = false; if ( _op == Instruction::CREATE ) result = e.createOpcode( myAddress, _endowment, gasPrice, io_gas, _code, origin ); @@ -185,7 +185,7 @@ h256 ExtVM::blockHash( u256 _number ) { if ( _number >= currentNumber || _number < ( std::max< u256 >( 256, currentNumber ) - 256 ) ) return h256(); - if ( currentNumber < m_sealEngine.chainParams().experimentalForkBlock + 256 ) { + if ( currentNumber < m_chainParams.experimentalForkBlock + 256 ) { h256 const parentHash = envInfo().header().parentHash(); h256s const lastHashes = envInfo().lastHashes().precedingHashes( parentHash ); @@ -199,6 +199,7 @@ h256 ExtVM::blockHash( u256 _number ) { tx.forceSender( caller ); ExecutionResult res; - std::tie( res, std::ignore ) = m_s.execute( envInfo(), m_sealEngine, tx, Permanence::Reverted ); + std::tie( res, std::ignore ) = + m_s.execute( envInfo(), m_chainParams, tx, Permanence::Reverted ); return h256( res.output ); } diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index 8f166359d..4076b0c10 100644 --- a/libethereum/ExtVM.h +++ b/libethereum/ExtVM.h @@ -37,15 +37,15 @@ class SealEngineFace; class ExtVM : public ExtVMFace { public: /// Full constructor. - ExtVM( skale::State& _s, EnvInfo const& _envInfo, SealEngineFace const& _sealEngine, + ExtVM( skale::State& _s, EnvInfo const& _envInfo, ChainOperationParams const& _chainParams, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, h256 const& _codeHash, u256 const& _version, unsigned _depth, bool _isCreate, bool _staticCall, bool _readOnly = true ) : ExtVMFace( _envInfo, _myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(), _codeHash, _version, _depth, _isCreate, _staticCall ), m_s( _s ), - m_sealEngine( _sealEngine ), - m_evmSchedule( initEvmSchedule( envInfo().number(), _version ) ), + m_chainParams( _chainParams ), + m_evmSchedule( initEvmSchedule( _version ) ), m_readOnly( _readOnly ) { // Contract: processing account must exist. In case of CALL, the ExtVM // is created only if an account has code (so exist). In case of CREATE @@ -95,9 +95,7 @@ class ExtVM : public ExtVMFace { virtual void suicide( Address _a ) override final; /// Return the EVM gas-price schedule for this execution context. - virtual EVMSchedule const& evmSchedule() const override final { - return m_sealEngine.evmSchedule( envInfo().number() ); - } + virtual EVMSchedule const& evmSchedule() const override final { return m_evmSchedule; } skale::State const& state() const { return m_s; } @@ -105,10 +103,11 @@ class ExtVM : public ExtVMFace { h256 blockHash( u256 _number ) override; private: - EVMSchedule const& initEvmSchedule( int64_t _blockNumber, u256 const& _version ) const { + EVMSchedule initEvmSchedule( u256 const& _version ) const { // If _version is latest for the block, select corresponding latest schedule. // Otherwise run with the latest schedule known to correspond to the _version. - EVMSchedule const& currentBlockSchedule = m_sealEngine.evmSchedule( _blockNumber ); + EVMSchedule currentBlockSchedule = m_chainParams.makeEvmSchedule( + envInfo().committedBlockTimestamp(), envInfo().number() ); if ( currentBlockSchedule.accountVersion == _version ) return currentBlockSchedule; else @@ -116,8 +115,8 @@ class ExtVM : public ExtVMFace { } skale::State& m_s; ///< A reference to the base state. - SealEngineFace const& m_sealEngine; - EVMSchedule const& m_evmSchedule; + ChainOperationParams const& m_chainParams; + EVMSchedule const m_evmSchedule; bool m_readOnly; }; diff --git a/libethereum/GasPricer.h b/libethereum/GasPricer.h index 4542861d0..237ddba80 100644 --- a/libethereum/GasPricer.h +++ b/libethereum/GasPricer.h @@ -41,7 +41,8 @@ class GasPricer { virtual ~GasPricer() = default; virtual u256 ask( Block const& ) const = 0; - virtual u256 bid( TransactionPriority _p = TransactionPriority::Medium ) const = 0; + virtual u256 bid( unsigned _blockNumber = dev::eth::LatestBlock, + TransactionPriority _p = TransactionPriority::Medium ) const = 0; virtual void update( BlockChain const& ) {} }; @@ -56,7 +57,10 @@ class TrivialGasPricer : public GasPricer { u256 ask() const { return m_ask; } u256 ask( Block const& ) const override { return m_ask; } - u256 bid( TransactionPriority = TransactionPriority::Medium ) const override { return m_bid; } + u256 bid( unsigned = dev::eth::LatestBlock, + TransactionPriority = TransactionPriority::Medium ) const override { + return m_bid; + } private: u256 m_ask = DefaultGasPrice; diff --git a/libethereum/Interface.h b/libethereum/Interface.h index a6c1c874f..018ccb934 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] @@ -242,7 +234,7 @@ class Interface { /// Get the remaining gas limit in this block. virtual u256 gasLimitRemaining() const = 0; // Get the gas bidding price - virtual u256 gasBidPrice() const = 0; + virtual u256 gasBidPrice( unsigned _blockNumber = dev::eth::LatestBlock ) const = 0; /// Get some information on the block queue. virtual SyncStatus syncStatus() const = 0; @@ -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/libethereum/Precompiled.cpp b/libethereum/Precompiled.cpp index aecc33209..ecbf7f770 100644 --- a/libethereum/Precompiled.cpp +++ b/libethereum/Precompiled.cpp @@ -36,8 +36,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -809,7 +809,9 @@ ETH_REGISTER_PRECOMPILED( getConfigVariableUint256 )( bytesConstRef _in ) { std::string strValue; // call to skaleConfig.sChain.nodes means call to the historic data // need to proccess it in a different way - if ( isCallToHistoricData( rawName ) && PrecompiledConfigPatch::isEnabled() ) { + // TODO Check if this precompiled can be called on historic block + if ( isCallToHistoricData( rawName ) && + PrecompiledConfigPatch::isEnabledInWorkingBlock() ) { if ( !g_skaleHost ) throw std::runtime_error( "SkaleHost accessor was not initialized" ); @@ -917,7 +919,9 @@ ETH_REGISTER_PRECOMPILED( getConfigVariableString )( bytesConstRef _in ) { std::string strValue; // call to skaleConfig.sChain.nodes means call to the historic data // need to proccess it in a different way - if ( isCallToHistoricData( rawName ) && PrecompiledConfigPatch::isEnabled() ) { + // TODO Check if this precompiled can be called on historic block + if ( isCallToHistoricData( rawName ) && + PrecompiledConfigPatch::isEnabledInWorkingBlock() ) { if ( !g_skaleHost ) throw std::runtime_error( "SkaleHost accessor was not initialized" ); diff --git a/libethereum/SchainPatch.cpp b/libethereum/SchainPatch.cpp index 393baf086..f3a5f8daa 100644 --- a/libethereum/SchainPatch.cpp +++ b/libethereum/SchainPatch.cpp @@ -1 +1,111 @@ #include "SchainPatch.h" + +#include + +using namespace dev::eth; + +ChainOperationParams SchainPatch::chainParams; +std::atomic< time_t > SchainPatch::committedBlockTimestamp; + +SchainPatchEnum getEnumForPatchName( const std::string& _patchName ) { + if ( _patchName == "RevertableFSPatch" ) + return SchainPatchEnum::RevertableFSPatch; + else if ( _patchName == "PrecompiledConfigPatch" ) + return SchainPatchEnum::PrecompiledConfigPatch; + else if ( _patchName == "PowCheckPatch" ) + return SchainPatchEnum::PowCheckPatch; + else if ( _patchName == "CorrectForkInPowPatch" ) + return SchainPatchEnum::CorrectForkInPowPatch; + else if ( _patchName == "ContractStorageZeroValuePatch" ) + return SchainPatchEnum::ContractStorageZeroValuePatch; + else if ( _patchName == "PushZeroPatch" ) + return SchainPatchEnum::PushZeroPatch; + else if ( _patchName == "ContractStoragePatch" ) + return SchainPatchEnum::ContractStoragePatch; + else if ( _patchName == "StorageDestructionPatch" ) + return SchainPatchEnum::StorageDestructionPatch; + else if ( _patchName == "SkipInvalidTransactionsPatch" ) + return SchainPatchEnum::SkipInvalidTransactionsPatch; + else if ( _patchName == "VerifyDaSigsPatch" ) + return SchainPatchEnum::VerifyDaSigsPatch; + else if ( _patchName == "FastConsensusPatch" ) + return SchainPatchEnum::FastConsensusPatch; + else if ( _patchName == "EIP1559TransactionsPatch" ) + return SchainPatchEnum::EIP1559TransactionsPatch; + else if ( _patchName == "VerifyBlsSyncPatch" ) + return SchainPatchEnum::VerifyBlsSyncPatch; + else if ( _patchName == "FlexibleDeploymentPatch" ) + return SchainPatchEnum::FlexibleDeploymentPatch; + else + throw std::out_of_range( _patchName ); +} + +std::string getPatchNameForEnum( SchainPatchEnum _enumValue ) { + switch ( _enumValue ) { + case SchainPatchEnum::RevertableFSPatch: + return "RevertableFSPatch"; + case SchainPatchEnum::PrecompiledConfigPatch: + return "PrecompiledConfigPatch"; + case SchainPatchEnum::PowCheckPatch: + return "PowCheckPatch"; + case SchainPatchEnum::CorrectForkInPowPatch: + return "CorrectForkInPowPatch"; + case SchainPatchEnum::ContractStorageZeroValuePatch: + return "ContractStorageZeroValuePatch"; + case SchainPatchEnum::PushZeroPatch: + return "PushZeroPatch"; + case SchainPatchEnum::ContractStoragePatch: + return "ContractStoragePatch"; + case SchainPatchEnum::StorageDestructionPatch: + return "StorageDestructionPatch"; + case SchainPatchEnum::SkipInvalidTransactionsPatch: + return "SkipInvalidTransactionsPatch"; + case SchainPatchEnum::SelfdestructStorageLimitPatch: + return "SelfdestructStorageLimitPatch"; + case SchainPatchEnum::VerifyDaSigsPatch: + return "VerifyDaSigsPatch"; + case SchainPatchEnum::FastConsensusPatch: + return "FastConsensusPatch"; + case SchainPatchEnum::EIP1559TransactionsPatch: + return "EIP1559TransactionsPatch"; + case SchainPatchEnum::VerifyBlsSyncPatch: + return "VerifyBlsSyncPatch"; + case SchainPatchEnum::FlexibleDeploymentPatch: + return "FlexibleDeploymentPatch"; + default: + throw std::out_of_range( + "UnknownPatch #" + std::to_string( static_cast< size_t >( _enumValue ) ) ); + } +} + +void SchainPatch::init( const dev::eth::ChainOperationParams& _cp ) { + chainParams = _cp; + for ( size_t i = 0; i < _cp.sChain._patchTimestamps.size(); ++i ) { + printInfo( getPatchNameForEnum( static_cast< SchainPatchEnum >( i ) ), + _cp.sChain._patchTimestamps[i] ); + } +} + +void SchainPatch::useLatestBlockTimestamp( time_t _timestamp ) { + committedBlockTimestamp = _timestamp; +} + +void SchainPatch::printInfo( const std::string& _patchName, time_t _timeStamp ) { + if ( _timeStamp == 0 ) { + cnote << "Patch " << _patchName << " is disabled"; + } else { + cnote << "Patch " << _patchName << " is set at timestamp " << _timeStamp; + } +} + +bool SchainPatch::isPatchEnabledWhen( + SchainPatchEnum _patchEnum, time_t _committedBlockTimestamp ) { + time_t activationTimestamp = chainParams.getPatchTimestamp( _patchEnum ); + return activationTimestamp != 0 && _committedBlockTimestamp >= activationTimestamp; +} + +EVMSchedule PushZeroPatch::makeSchedule( const EVMSchedule& _base ) { + EVMSchedule ret = _base; + ret.havePush0 = true; + return ret; +} diff --git a/libethereum/SchainPatch.h b/libethereum/SchainPatch.h index 5bafcdcd0..9af149fb9 100644 --- a/libethereum/SchainPatch.h +++ b/libethereum/SchainPatch.h @@ -1,17 +1,149 @@ #ifndef SCHAINPATCH_H #define SCHAINPATCH_H +#include "SchainPatchEnum.h" + +#include + #include +#include + +namespace dev { +namespace eth { +struct EVMSchedule; +} +} // namespace dev + + class SchainPatch { public: - static void printInfo( const std::string& _patchName, time_t _timeStamp ) { - if ( _timeStamp == 0 ) { - cnote << "Patch " << _patchName << " is disabled"; - } else { - cnote << "Patch " << _patchName << " is set at timestamp " << _timeStamp; - } + static void init( const dev::eth::ChainOperationParams& _cp ); + static void useLatestBlockTimestamp( time_t _timestamp ); + + static SchainPatchEnum getEnumForPatchName( const std::string& _patchName ); + +protected: + static void printInfo( const std::string& _patchName, time_t _timeStamp ); + static bool isPatchEnabledInWorkingBlock( SchainPatchEnum _patchEnum ) { + time_t activationTimestamp = chainParams.getPatchTimestamp( _patchEnum ); + return activationTimestamp != 0 && committedBlockTimestamp >= activationTimestamp; } + static bool isPatchEnabledWhen( SchainPatchEnum _patchEnum, time_t _committedBlockTimestamp ); + +protected: + static dev::eth::ChainOperationParams chainParams; + static std::atomic< time_t > committedBlockTimestamp; }; +#define DEFINE_AMNESIC_PATCH( CustomPatch ) \ + class CustomPatch : public SchainPatch { \ + public: \ + static SchainPatchEnum getEnum() { return SchainPatchEnum::CustomPatch; } \ + static bool isEnabledInWorkingBlock() { \ + return isPatchEnabledInWorkingBlock( getEnum() ); \ + } \ + }; + +// TODO One more overload - with EnvInfo? +#define DEFINE_SIMPLE_PATCH( CustomPatch ) \ + class CustomPatch : public SchainPatch { \ + public: \ + static SchainPatchEnum getEnum() { return SchainPatchEnum::CustomPatch; } \ + static bool isEnabledInWorkingBlock() { \ + return isPatchEnabledInWorkingBlock( getEnum() ); \ + } \ + static bool isEnabledWhen( time_t _committedBlockTimestamp ) { \ + return isPatchEnabledWhen( getEnum(), _committedBlockTimestamp ); \ + } \ + }; + +#define DEFINE_EVM_PATCH( CustomPatch ) \ + class CustomPatch : public SchainPatch { \ + public: \ + static SchainPatchEnum getEnum() { return SchainPatchEnum::CustomPatch; } \ + static bool isEnabledInWorkingBlock() { \ + return isPatchEnabledInWorkingBlock( getEnum() ); \ + } \ + static bool isEnabledWhen( time_t _committedBlockTimestamp ) { \ + return isPatchEnabledWhen( getEnum(), _committedBlockTimestamp ); \ + } \ + static dev::eth::EVMSchedule makeSchedule( const dev::eth::EVMSchedule& base ); \ + }; + +/* + * Context: enable revertable filestorage precompileds + */ +DEFINE_SIMPLE_PATCH( RevertableFSPatch ) + +/* + * Context: enable precompiled contracts to read historical config data + */ +DEFINE_AMNESIC_PATCH( PrecompiledConfigPatch ) + +/* + * Context: enable fix for POW txns gas limit check + */ +DEFINE_SIMPLE_PATCH( PowCheckPatch ) + +/* + * Context: use current, and not Constantinople, fork in Transaction::checkOutExternalGas() + */ +DEFINE_SIMPLE_PATCH( CorrectForkInPowPatch ) + +/* + * Context: contractStorageUsed counter didn't work well in one case + * Solution: we fixed the bug and added new config field introudceChangesTimestamp + * Purpose: avoid incorrect txn behaviour + * Version introduced: + */ +DEFINE_AMNESIC_PATCH( ContractStorageZeroValuePatch ) + +/* + * Context: enable effective storage destruction + */ +DEFINE_EVM_PATCH( PushZeroPatch ) + +/* + * Context: contractStorageUsed counter didn't work well in one case + * Solution: we fixed the bug and added new config field introudceChangesTimestamp + * Purpose: avoid incorrect txn behaviour + * Version introduced: + */ +DEFINE_SIMPLE_PATCH( VerifyDaSigsPatch ) + +/* + * Context: contractStorageUsed counter didn't work well in one case + * Solution: we fixed the bug and added new config field introudceChangesTimestamp + * Purpose: avoid incorrect txn behaviour + * Version introduced: + */ +DEFINE_AMNESIC_PATCH( ContractStoragePatch ) + +/* + * Context: enable effective storage destruction + */ +DEFINE_AMNESIC_PATCH( StorageDestructionPatch ); + +/* + * Enable restriction on contract storage size, when it's doing selfdestruct + */ +DEFINE_SIMPLE_PATCH( SelfdestructStorageLimitPatch ); + +/* + * Enable restriction on contract storage size, when it's doing selfdestruct + */ +DEFINE_SIMPLE_PATCH( EIP1559TransactionsPatch ); + +/* + * Enable bls signatures verification for sync node + */ +DEFINE_AMNESIC_PATCH( VerifyBlsSyncPatch ); + +/* + * Purpose: passing both transaction origin and sender to the ConfigController contract + * Version introduced: 3.19.0 + */ +DEFINE_SIMPLE_PATCH( FlexibleDeploymentPatch ); + #endif // SCHAINPATCH_H diff --git a/libethereum/SchainPatchEnum.h b/libethereum/SchainPatchEnum.h new file mode 100644 index 000000000..ac0b1c19a --- /dev/null +++ b/libethereum/SchainPatchEnum.h @@ -0,0 +1,30 @@ +#ifndef SCHAINPATCHENUM_H +#define SCHAINPATCHENUM_H + +#include +#include + +enum class SchainPatchEnum { + RevertableFSPatch, + PrecompiledConfigPatch, + PowCheckPatch, + CorrectForkInPowPatch, + ContractStorageZeroValuePatch, + PushZeroPatch, + ContractStoragePatch, + StorageDestructionPatch, + SkipInvalidTransactionsPatch, + SelfdestructStorageLimitPatch, + VerifyDaSigsPatch, + FastConsensusPatch, + EIP1559TransactionsPatch, + VerifyBlsSyncPatch, + FlexibleDeploymentPatch, + PatchesCount +}; + +extern SchainPatchEnum getEnumForPatchName( const std::string& _patchName ); +extern std::string getPatchNameForEnum( SchainPatchEnum enumValue ); + + +#endif // SCHAINPATCHENUM_H diff --git a/libethereum/SkaleHost.cpp b/libethereum/SkaleHost.cpp index b1a140d6d..47714ebfe 100644 --- a/libethereum/SkaleHost.cpp +++ b/libethereum/SkaleHost.cpp @@ -47,7 +47,6 @@ using namespace std; #include #include #include -#include #include @@ -81,8 +80,11 @@ std::unique_ptr< ConsensusInterface > DefaultConsensusFactory::create( std::map< std::string, std::uint64_t > patchTimeStamps; patchTimeStamps["verifyDaSigsPatchTimestamp"] = - VerifyDaSigsPatch::getVerifyDaSigsPatchTimestamp(); - + m_client.chainParams().getPatchTimestamp( SchainPatchEnum::VerifyDaSigsPatch ); + patchTimeStamps["fastConsensusPatchTimestamp"] = + m_client.chainParams().getPatchTimestamp( SchainPatchEnum::FastConsensusPatch ); + patchTimeStamps["verifyBlsSyncPatchTimestamp"] = + m_client.chainParams().getPatchTimestamp( SchainPatchEnum::VerifyBlsSyncPatch ); auto consensus_engine_ptr = make_unique< ConsensusEngine >( _extFace, m_client.number(), ts, 0, patchTimeStamps, m_client.chainParams().sChain.consensusStorageLimit ); @@ -91,10 +93,8 @@ std::unique_ptr< ConsensusInterface > DefaultConsensusFactory::create( this->fillSgxInfo( *consensus_engine_ptr ); } - this->fillPublicKeyInfo( *consensus_engine_ptr ); - this->fillRotationHistory( *consensus_engine_ptr ); return consensus_engine_ptr; @@ -145,13 +145,15 @@ void DefaultConsensusFactory::fillSgxInfo( ConsensusEngine& consensus ) const tr } void DefaultConsensusFactory::fillPublicKeyInfo( ConsensusEngine& consensus ) const try { + if ( m_client.chainParams().nodeInfo.testSignatures ) + // no keys in tests + return; + const std::string sgxServerUrl = m_client.chainParams().nodeInfo.sgxServerUrl; std::shared_ptr< std::vector< std::string > > ecdsaPublicKeys = std::make_shared< std::vector< std::string > >(); for ( const auto& node : m_client.chainParams().sChain.nodes ) { - if ( node.publicKey.size() == 0 ) - return; // just don't do anything ecdsaPublicKeys->push_back( node.publicKey.substr( 2 ) ); } @@ -159,15 +161,15 @@ void DefaultConsensusFactory::fillPublicKeyInfo( ConsensusEngine& consensus ) co for ( const auto& node : m_client.chainParams().sChain.nodes ) { std::vector< std::string > public_key_share( 4 ); if ( node.id != this->m_client.chainParams().nodeInfo.id ) { - public_key_share[0] = node.blsPublicKey[0]; - public_key_share[1] = node.blsPublicKey[1]; - public_key_share[2] = node.blsPublicKey[2]; - public_key_share[3] = node.blsPublicKey[3]; + public_key_share[0] = node.blsPublicKey.at( 0 ); + public_key_share[1] = node.blsPublicKey.at( 1 ); + public_key_share[2] = node.blsPublicKey.at( 2 ); + public_key_share[3] = node.blsPublicKey.at( 3 ); } else { - public_key_share[0] = m_client.chainParams().nodeInfo.BLSPublicKeys[0]; - public_key_share[1] = m_client.chainParams().nodeInfo.BLSPublicKeys[1]; - public_key_share[2] = m_client.chainParams().nodeInfo.BLSPublicKeys[2]; - public_key_share[3] = m_client.chainParams().nodeInfo.BLSPublicKeys[3]; + public_key_share[0] = m_client.chainParams().nodeInfo.BLSPublicKeys.at( 0 ); + public_key_share[1] = m_client.chainParams().nodeInfo.BLSPublicKeys.at( 1 ); + public_key_share[2] = m_client.chainParams().nodeInfo.BLSPublicKeys.at( 2 ); + public_key_share[3] = m_client.chainParams().nodeInfo.BLSPublicKeys.at( 3 ); } blsPublicKeys.push_back( @@ -181,11 +183,10 @@ void DefaultConsensusFactory::fillPublicKeyInfo( ConsensusEngine& consensus ) co size_t n = m_client.chainParams().sChain.nodes.size(); size_t t = ( 2 * n + 1 ) / 3; - if ( ecdsaPublicKeys->size() && ecdsaPublicKeys->at( 0 ).size() && blsPublicKeys.size() && - blsPublicKeys[0]->at( 0 ).size() ) - consensus.setPublicKeyInfo( ecdsaPublicKeys, blsPublicKeysPtr, t, n ); + consensus.setPublicKeyInfo( + ecdsaPublicKeys, blsPublicKeysPtr, t, n, m_client.chainParams().nodeInfo.syncNode ); } catch ( ... ) { - std::throw_with_nested( std::runtime_error( "Error filling SGX info (nodeGroups)" ) ); + std::throw_with_nested( std::runtime_error( "Error filling public keys info (nodeGroups)" ) ); } @@ -195,8 +196,9 @@ void DefaultConsensusFactory::fillRotationHistory( ConsensusEngine& consensus ) std::map< uint64_t, std::vector< uint64_t > > historicNodeGroups; auto u256toUint64 = []( const dev::u256& u ) { return std::stoull( u.str() ); }; for ( const auto& nodeGroup : m_client.chainParams().sChain.nodeGroups ) { - std::vector< string > commonBLSPublicKey = { nodeGroup.blsPublicKey[0], - nodeGroup.blsPublicKey[1], nodeGroup.blsPublicKey[2], nodeGroup.blsPublicKey[3] }; + std::vector< string > commonBLSPublicKey = { nodeGroup.blsPublicKey.at( 0 ), + nodeGroup.blsPublicKey.at( 1 ), nodeGroup.blsPublicKey.at( 2 ), + nodeGroup.blsPublicKey.at( 3 ) }; previousBLSKeys[nodeGroup.finishTs] = commonBLSPublicKey; std::vector< uint64_t > nodes; // add ecdsa keys info and historic groups info @@ -334,8 +336,8 @@ h256 SkaleHost::receiveTransaction( std::string _rlp ) { return h256(); } - Transaction transaction( jsToBytes( _rlp, OnFailed::Throw ), CheckTransaction::None ); - + Transaction transaction( jsToBytes( _rlp, OnFailed::Throw ), CheckTransaction::None, false, + EIP1559TransactionsPatch::isEnabledInWorkingBlock() ); h256 sha = transaction.sha3(); // @@ -433,9 +435,10 @@ ConsensusExtFace::transactions_vector SkaleHost::pendingTransactions( m_debugTracer.tracepoint( "fetch_transactions" ); int counter = 0; + BlockHeader latestInfo = static_cast< const Interface& >( m_client ).blockInfo( LatestBlock ); Transactions txns = m_tq.topTransactionsSync( - _limit, [this, &to_delete, &counter]( const Transaction& tx ) -> bool { + _limit, [this, &to_delete, &counter, &latestInfo]( const Transaction& tx ) -> bool { if ( m_tq.getCategory( tx.sha3() ) != 1 ) // take broadcasted return false; @@ -446,9 +449,8 @@ ConsensusExtFace::transactions_vector SkaleHost::pendingTransactions( if ( tx.verifiedOn < m_lastBlockWithBornTransactions ) try { bool isMtmEnabled = m_client.chainParams().sChain.multiTransactionMode; - Executive::verifyTransaction( tx, - static_cast< const Interface& >( m_client ).blockInfo( LatestBlock ), - m_client.state().createStateReadOnlyCopy(), *m_client.sealEngine(), 0, + Executive::verifyTransaction( tx, latestInfo.timestamp(), latestInfo, + m_client.state().createStateReadOnlyCopy(), m_client.chainParams(), 0, getGasPrice(), isMtmEnabled ); } catch ( const exception& ex ) { if ( to_delete.count( tx.sha3() ) == 0 ) @@ -526,7 +528,7 @@ ConsensusExtFace::transactions_vector SkaleHost::pendingTransactions( m_m_transaction_cache[sha.asArray()] = txn; } - out_vector.push_back( txn.rlp() ); + out_vector.push_back( txn.toBytes() ); ++total_sent; @@ -628,6 +630,9 @@ void SkaleHost::createBlock( const ConsensusExtFace::transactions_vector& _appro // HACK this is for not allowing new transactions in tq between deletion and block creation! // TODO decouple SkaleHost and Client!!! size_t n_succeeded; + + BlockHeader latestInfo = static_cast< const Interface& >( m_client ).blockInfo( LatestBlock ); + DEV_GUARDED( m_client.m_blockImportMutex ) { m_debugTracer.tracepoint( "drop_good_transactions" ); @@ -653,7 +658,8 @@ 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 ); + t.checkOutExternalGas( + m_client.chainParams(), latestInfo.timestamp(), m_client.number(), true ); out_txns.push_back( t ); LOG( m_debugLogger ) << "Dropping good txn " << sha << std::endl; m_debugTracer.tracepoint( "drop_good" ); @@ -666,8 +672,10 @@ void SkaleHost::createBlock( const ConsensusExtFace::transactions_vector& _appro // for test std::thread( [t, this]() { m_client.importTransaction( t ); } // ).detach(); } else { - Transaction t( data, CheckTransaction::Everything, true ); - t.checkOutExternalGas( m_client.chainParams(), m_client.number() ); + Transaction t( data, CheckTransaction::Everything, true, + EIP1559TransactionsPatch::isEnabledInWorkingBlock() ); + t.checkOutExternalGas( + m_client.chainParams(), latestInfo.timestamp(), m_client.number(), false ); out_txns.push_back( t ); LOG( m_debugLogger ) << "Will import consensus-born txn"; m_debugTracer.tracepoint( "import_consensus_born" ); @@ -893,7 +901,7 @@ void SkaleHost::broadcastFunc() { if ( !m_broadcastPauseFlag ) { MICROPROFILE_SCOPEI( "SkaleHost", "broadcastFunc.broadcast", MP_CHARTREUSE1 ); - std::string rlp = toJS( txn.rlp() ); + std::string rlp = toJS( txn.toBytes() ); std::string h = toJS( txn.sha3() ); // std::string strPerformanceQueueName = "bc/broadcast"; @@ -909,8 +917,8 @@ void SkaleHost::broadcastFunc() { m_broadcaster->broadcast( rlp ); } } catch ( const std::exception& ex ) { - cwarn << "BROADCAST EXCEPTION CAUGHT" << endl; - cwarn << ex.what() << endl; + cwarn << "BROADCAST EXCEPTION CAUGHT"; + cwarn << ex.what(); } // catch } // if @@ -934,8 +942,10 @@ void SkaleHost::broadcastFunc() { m_broadcaster->stopService(); } -u256 SkaleHost::getGasPrice() const { - return m_consensus->getPriceForBlockId( m_client.number() ); +u256 SkaleHost::getGasPrice( unsigned _blockNumber ) const { + if ( _blockNumber == dev::eth::LatestBlock ) + _blockNumber = m_client.number(); + return m_consensus->getPriceForBlockId( _blockNumber ); } u256 SkaleHost::getBlockRandom() const { @@ -994,7 +1004,7 @@ void SkaleHost::forceEmptyBlock() { } void SkaleHost::forcedBroadcast( const Transaction& _txn ) { - m_broadcaster->broadcast( toJS( _txn.rlp() ) ); + m_broadcaster->broadcast( toJS( _txn.toBytes() ) ); } void SkaleHost::noteNewTransactions() {} diff --git a/libethereum/SkaleHost.h b/libethereum/SkaleHost.h index 8ffce7aca..598bf47ad 100644 --- a/libethereum/SkaleHost.h +++ b/libethereum/SkaleHost.h @@ -122,7 +122,7 @@ class SkaleHost { dev::h256 receiveTransaction( std::string ); - dev::u256 getGasPrice() const; + dev::u256 getGasPrice( unsigned _blockNumber = dev::eth::LatestBlock ) const; dev::u256 getBlockRandom() const; dev::eth::SyncStatus syncStatus() const; std::map< std::string, uint64_t > getConsensusDbUsage() const; diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index dc41a2838..d2c7f9962 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -28,8 +28,8 @@ #include #include #include +#include #include -#include using namespace std; using namespace dev; @@ -164,11 +164,13 @@ Transaction::Transaction( const u256& _value, const u256& _gasPrice, const u256& const bytes& _data, const u256& _nonce ) : TransactionBase( _value, _gasPrice, _gas, _data, _nonce ) {} -Transaction::Transaction( bytesConstRef _rlpData, CheckTransaction _checkSig, bool _allowInvalid ) - : TransactionBase( _rlpData, _checkSig, _allowInvalid ) {} +Transaction::Transaction( + bytesConstRef _rlpData, CheckTransaction _checkSig, bool _allowInvalid, bool _eip1559Enabled ) + : TransactionBase( _rlpData, _checkSig, _allowInvalid, _eip1559Enabled ) {} -Transaction::Transaction( const bytes& _rlp, CheckTransaction _checkSig, bool _allowInvalid ) - : Transaction( &_rlp, _checkSig, _allowInvalid ) {} +Transaction::Transaction( + const bytes& _rlp, CheckTransaction _checkSig, bool _allowInvalid, bool _eip1559Enabled ) + : Transaction( &_rlp, _checkSig, _allowInvalid, _eip1559Enabled ) {} bool Transaction::hasExternalGas() const { if ( !m_externalGasIsChecked ) { @@ -193,7 +195,8 @@ u256 Transaction::gasPrice() const { } } -void Transaction::checkOutExternalGas( const ChainParams& _cp, uint64_t _bn, bool _force ) { +void Transaction::checkOutExternalGas( const ChainParams& _cp, time_t _committedBlockTimestamp, + uint64_t _committedBlockNumber, bool _force ) { u256 const& difficulty = _cp.externalGasDifficulty; assert( difficulty > 0 ); if ( ( _force || !m_externalGasIsChecked ) && !isInvalid() ) { @@ -203,21 +206,12 @@ void Transaction::checkOutExternalGas( const ChainParams& _cp, uint64_t _bn, boo } u256 externalGas = ~u256( 0 ) / u256( hash ) / difficulty; if ( externalGas > 0 ) - ctrace << "Mined gas: " << externalGas << endl; + ctrace << "Mined gas: " << externalGas; EVMSchedule scheduleForUse = ConstantinopleSchedule; - if ( CorrectForkInPowPatch::isEnabled() ) - scheduleForUse = _cp.scheduleForBlockNumber( _bn ); - - -#ifndef HISTORIC_STATE // FIX FOR 2.3.1. Will not be needed in 2.4 - // 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" ) ); - } -#endif + if ( CorrectForkInPowPatch::isEnabledWhen( _committedBlockTimestamp ) ) + scheduleForUse = _cp.makeEvmSchedule( + _committedBlockTimestamp, _committedBlockNumber ); // BUG should be +1 if ( externalGas >= baseGasRequired( scheduleForUse ) ) m_externalGas = externalGas; diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index dffadb347..9d3894f31 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -107,12 +107,12 @@ class Transaction : public TransactionBase { u256 const& _nonce = Invalid256 ); /// Constructs a transaction from the given RLP. - explicit Transaction( - bytesConstRef _rlp, CheckTransaction _checkSig, bool _allowInvalid = false ); + explicit Transaction( bytesConstRef _rlp, CheckTransaction _checkSig, + bool _allowInvalid = false, bool _eip1559Enabled = false ); /// Constructs a transaction from the given RLP. - explicit Transaction( - bytes const& _rlp, CheckTransaction _checkSig, bool _allowInvalid = false ); + explicit Transaction( bytes const& _rlp, CheckTransaction _checkSig, bool _allowInvalid = false, + bool _eip1559Enabled = false ); Transaction( Transaction const& ) = default; @@ -122,7 +122,8 @@ class Transaction : public TransactionBase { u256 gasPrice() const; - void checkOutExternalGas( const ChainParams& _cp, uint64_t _bn, bool _force = false ); + void checkOutExternalGas( const ChainParams& _cp, time_t _committedBlockTimestamp, + uint64_t _committedBlockNumber, bool _force ); void ignoreExternalGas() { m_externalGasIsChecked = true; diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 6a075bf84..16e726e76 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -26,6 +26,7 @@ #include "Transaction.h" #include #include +#include #include #include @@ -94,7 +95,8 @@ void TransactionQueue::HandleDestruction() { ImportResult TransactionQueue::import( bytesConstRef _transactionRLP, IfDropped _ik, bool _isFuture ) { try { - Transaction t = Transaction( _transactionRLP, CheckTransaction::Everything ); + Transaction t = Transaction( _transactionRLP, CheckTransaction::Everything, false, + EIP1559TransactionsPatch::isEnabledInWorkingBlock() ); return import( t, _ik, _isFuture ); } catch ( Exception const& ) { return ImportResult::Malformed; @@ -131,17 +133,15 @@ ImportResult TransactionQueue::import( // if transaction found: if ( t != fs->second.end() ) { - // if( t == fs->second.begin() ){ UpgradeGuard ul( l ); --m_futureSize; - m_futureSizeBytes -= t->second.transaction.rlp().size(); + m_futureSizeBytes -= t->second.transaction.toBytes().size(); auto erasedHash = t->second.transaction.sha3(); LOG( m_loggerDetail ) << "Re-inserting future transaction " << erasedHash; m_known.erase( erasedHash ); fs->second.erase( t->second.transaction.nonce() ); if ( fs->second.empty() ) m_future.erase( fs ); - // } } // if found } // if fs->second @@ -327,7 +327,7 @@ void TransactionQueue::insertCurrent_WITH_LOCK( std::pair< h256, Transaction > c inserted.first->second = handle; m_currentByHash[_p.first] = handle; #pragma GCC diagnostic pop - m_currentSizeBytes += t.rlp().size(); + m_currentSizeBytes += t.toBytes().size(); // Move following transactions from future to current makeCurrent_WITH_LOCK( t ); @@ -345,7 +345,7 @@ bool TransactionQueue::remove_WITH_LOCK( h256 const& _txHash ) { auto it = m_currentByAddressAndNonce.find( from ); assert( it != m_currentByAddressAndNonce.end() ); it->second.erase( ( *t->second ).transaction.nonce() ); - m_currentSizeBytes -= ( *t->second ).transaction.rlp().size(); + m_currentSizeBytes -= ( *t->second ).transaction.toBytes().size(); m_current.erase( t->second ); m_currentByHash.erase( t ); if ( it->second.empty() ) @@ -382,8 +382,8 @@ void TransactionQueue::setFuture_WITH_LOCK( h256 const& _txHash ) { *( m->second ) ); // set has only const iterators. Since we are moving out of container // that's fine m_currentByHash.erase( t.transaction.sha3() ); - m_currentSizeBytes -= t.transaction.rlp().size(); - m_futureSizeBytes += t.transaction.rlp().size(); + m_currentSizeBytes -= t.transaction.toBytes().size(); + m_futureSizeBytes += t.transaction.toBytes().size(); target.emplace( t.transaction.nonce(), move( t ) ); m_current.erase( m->second ); ++m_futureSize; @@ -396,7 +396,7 @@ void TransactionQueue::setFuture_WITH_LOCK( h256 const& _txHash ) { // TODO: priority queue for future transactions // For now just drop random chain end --m_futureSize; - m_futureSizeBytes -= m_future.begin()->second.rbegin()->second.transaction.rlp().size(); + m_futureSizeBytes -= m_future.begin()->second.rbegin()->second.transaction.toBytes().size(); auto erasedHash = m_future.begin()->second.rbegin()->second.transaction.sha3(); LOG( m_loggerDetail ) << "Dropping out of bounds future transaction " << erasedHash; m_known.erase( erasedHash ); @@ -430,8 +430,8 @@ void TransactionQueue::makeCurrent_WITH_LOCK( Transaction const& _t ) { inserted.first->second = handle; m_currentByHash[( *handle ).transaction.sha3()] = handle; #pragma GCC diagnostic pop - m_futureSizeBytes -= ( *handle ).transaction.rlp().size(); - m_currentSizeBytes += ( *handle ).transaction.rlp().size(); + m_futureSizeBytes -= ( *handle ).transaction.toBytes().size(); + m_currentSizeBytes += ( *handle ).transaction.toBytes().size(); --m_futureSize; ++ft; ++nonce; @@ -526,8 +526,9 @@ void TransactionQueue::verifierBody() { } // block try { - Transaction t( work.transaction, CheckTransaction::Cheap ); // Signature will be - // checked later + Transaction t( work.transaction, CheckTransaction::Cheap, false, + EIP1559TransactionsPatch::isEnabledInWorkingBlock() ); // Signature will be + // checked later ImportResult ir = import( t ); m_onImport( ir, t.sha3(), work.nodeId ); } catch ( ... ) { diff --git a/libethereum/TransactionReceipt.h b/libethereum/TransactionReceipt.h index dcbeff84c..76cb0592f 100644 --- a/libethereum/TransactionReceipt.h +++ b/libethereum/TransactionReceipt.h @@ -97,7 +97,8 @@ class LocalisedTransactionReceipt : public TransactionReceipt { public: LocalisedTransactionReceipt( TransactionReceipt const& _t, h256 const& _hash, h256 const& _blockHash, BlockNumber _blockNumber, unsigned _transactionIndex, Address _from, - Address _to, u256 const& _gasUsed, Address const& _contractAddress = Address() ) + Address _to, u256 const& _gasUsed, Address const& _contractAddress = Address(), + int _txType = 0, u256 _effectiveGasPrice = 0 ) : TransactionReceipt( _t ), m_hash( _hash ), m_blockHash( _blockHash ), @@ -106,7 +107,9 @@ class LocalisedTransactionReceipt : public TransactionReceipt { m_from( _from ), m_to( _to ), m_gasUsed( _gasUsed ), - m_contractAddress( _contractAddress ) { + m_contractAddress( _contractAddress ), + m_txType( _txType ), + m_effectiveGasPrice( _effectiveGasPrice ) { LogEntries entries = log(); for ( unsigned i = 0; i < entries.size(); i++ ) m_localisedLogs.push_back( LocalisedLogEntry( @@ -124,6 +127,8 @@ class LocalisedTransactionReceipt : public TransactionReceipt { u256 const& gasUsed() const { return m_gasUsed; } Address const& contractAddress() const { return m_contractAddress; } LocalisedLogEntries const& localisedLogs() const { return m_localisedLogs; }; + int txType() const { return m_txType; } + u256 effectiveGasPrice() const { return m_effectiveGasPrice; } private: h256 m_hash; @@ -134,6 +139,8 @@ class LocalisedTransactionReceipt : public TransactionReceipt { u256 m_gasUsed; Address m_contractAddress; LocalisedLogEntries m_localisedLogs; + int m_txType; + u256 m_effectiveGasPrice = 0; Counter< TransactionReceipt > c; diff --git a/libethereum/ValidationSchemes.cpp b/libethereum/ValidationSchemes.cpp index bcd49dd33..e6b493e7e 100644 --- a/libethereum/ValidationSchemes.cpp +++ b/libethereum/ValidationSchemes.cpp @@ -17,7 +17,13 @@ along with cpp-ethereum. If not, see . */ #include "ValidationSchemes.h" + +#include + #include + +#include + #include using namespace std; @@ -158,7 +164,7 @@ void validateConfigJson( js::mObject const& _obj ) { { "snapshotIntervalSec", { { js::int_type }, JsonFieldPresence::Optional } }, { "rotateAfterBlock", { { js::int_type }, JsonFieldPresence::Optional } }, { "wallets", { { js::obj_type }, JsonFieldPresence::Optional } }, - { "ecdsaKeyName", { { js::str_type }, JsonFieldPresence::Required } }, + { "ecdsaKeyName", { { js::str_type }, JsonFieldPresence::Optional } }, { "verifyImaMessagesViaLogsSearch", { { js::bool_type }, JsonFieldPresence::Optional } }, { "verifyImaMessagesViaContractCall", @@ -216,6 +222,7 @@ void validateConfigJson( js::mObject const& _obj ) { { "syncNode", { { js::bool_type }, JsonFieldPresence::Optional } }, { "archiveMode", { { js::bool_type }, JsonFieldPresence::Optional } }, { "syncFromCatchup", { { js::bool_type }, JsonFieldPresence::Optional } }, + { "testSignatures", { { js::bool_type }, JsonFieldPresence::Optional } }, { "wallets", { { js::obj_type }, JsonFieldPresence::Optional } } } ); std::string keyShareName = ""; @@ -254,7 +261,6 @@ void validateConfigJson( js::mObject const& _obj ) { { { js::int_type }, JsonFieldPresence::Optional } }, { "rotateAfterBlock", { { js::int_type }, JsonFieldPresence::Optional } }, { "contractStorageLimit", { { js::int_type }, JsonFieldPresence::Optional } }, - { "contractStoragePatchTimestamp", { { js::int_type }, JsonFieldPresence::Optional } }, { "dbStorageLimit", { { js::int_type }, JsonFieldPresence::Optional } }, { "nodes", { { js::array_type }, JsonFieldPresence::Required } }, { "maxConsensusStorageBytes", { { js::int_type }, JsonFieldPresence::Optional } }, @@ -263,22 +269,19 @@ void validateConfigJson( js::mObject const& _obj ) { { "maxSkaledLeveldbStorageBytes", { { js::int_type }, JsonFieldPresence::Optional } }, { "freeContractDeployment", { { js::bool_type }, JsonFieldPresence::Optional } }, { "multiTransactionMode", { { js::bool_type }, JsonFieldPresence::Optional } }, - { "revertableFSPatchTimestamp", { { js::int_type }, JsonFieldPresence::Optional } }, - { "contractStorageZeroValuePatchTimestamp", - { { js::int_type }, JsonFieldPresence::Optional } }, - { "verifyDaSigsPatchTimestamp", { { js::int_type }, JsonFieldPresence::Optional } }, - { "storageDestructionPatchTimestamp", - { { js::int_type }, JsonFieldPresence::Optional } }, - { "powCheckPatchTimestamp", { { js::int_type }, JsonFieldPresence::Optional } }, - { "pushZeroPatchTimestamp", { { js::int_type }, JsonFieldPresence::Optional } }, - { "nodeGroups", { { js::obj_type }, JsonFieldPresence::Optional } }, - { "nodeGroups", { { js::obj_type }, JsonFieldPresence::Optional } }, - { "skipInvalidTransactionsPatchTimestamp", - { { js::int_type }, JsonFieldPresence::Optional } }, - { "precompiledConfigPatchTimestamp", - { { js::int_type }, JsonFieldPresence::Optional } }, - { "correctForkInPowPatchTimestamp", - { { js::int_type }, JsonFieldPresence::Optional } } } ); + { "nodeGroups", { { js::obj_type }, JsonFieldPresence::Optional } } }, + []( const string& _key ) { + // function fow allowing fields + // exception means bad name + try { + string patchName = boost::algorithm::erase_last_copy( _key, "Timestamp" ); + patchName[0] = toupper( patchName[0] ); + getEnumForPatchName( patchName ); + return true; + } catch ( const std::out_of_range& ) { + return false; + } + } ); js::mArray const& nodes = sChain.at( "nodes" ).get_array(); for ( auto const& obj : nodes ) { diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 8d3f22bd9..4771e9004 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -131,17 +131,19 @@ struct CallParameters { class EnvInfo { public: - EnvInfo( BlockHeader const& _current, LastBlockHashesFace const& _lh, u256 const& _gasUsed, - u256 const& _chainID ) - : m_headerInfo( _current ), + EnvInfo( BlockHeader const& _working, LastBlockHashesFace const& _lh, + time_t _committedBlockTimestamp, u256 const& _gasUsed, u256 const& _chainID ) + : m_headerInfo( _working ), m_lastHashes( _lh ), + m_committedBlockTimestamp( _committedBlockTimestamp ), m_gasUsed( _gasUsed ), m_chainID( _chainID ) {} // Constructor with custom gasLimit - used in some synthetic scenarios like eth_estimateGas RPC // method - EnvInfo( BlockHeader const& _current, LastBlockHashesFace const& _lh, u256 const& _gasUsed, - u256 const& _gasLimit, u256 const& _chainID ) - : EnvInfo( _current, _lh, _gasUsed, _chainID ) { + EnvInfo( BlockHeader const& _working, LastBlockHashesFace const& _lh, + time_t _committedBlockTimestamp, u256 const& _gasUsed, u256 const& _gasLimit, + u256 const& _chainID ) + : EnvInfo( _working, _lh, _committedBlockTimestamp, _gasUsed, _chainID ) { m_headerInfo.setGasLimit( _gasLimit ); } @@ -153,12 +155,14 @@ class EnvInfo { u256 const& difficulty() const { return m_headerInfo.difficulty(); } u256 const& gasLimit() const { return m_headerInfo.gasLimit(); } LastBlockHashesFace const& lastHashes() const { return m_lastHashes; } + time_t committedBlockTimestamp() const { return m_committedBlockTimestamp; } u256 const& gasUsed() const { return m_gasUsed; } u256 const& chainID() const { return m_chainID; } private: BlockHeader m_headerInfo; LastBlockHashesFace const& m_lastHashes; + time_t m_committedBlockTimestamp; u256 m_gasUsed; u256 m_chainID; }; diff --git a/libevm/LegacyVM.cpp b/libevm/LegacyVM.cpp index 2fdfb429c..a6d64254d 100644 --- a/libevm/LegacyVM.cpp +++ b/libevm/LegacyVM.cpp @@ -16,7 +16,7 @@ */ #include "LegacyVM.h" -#include "libskale/PushZeroPatch.h" +#include using namespace std; using namespace dev; @@ -1374,7 +1374,7 @@ void LegacyVM::interpretCases() { // we need to increment program counter only by one since // the value is not read from program code as in PUSH1 CASE( PUSH0 ) { - if ( !PushZeroPatch::isEnabled() ) { + if ( !m_schedule->havePush0 ) { throwBadInstruction(); } ON_OP(); diff --git a/libhistoric/AlethExecutive.cpp b/libhistoric/AlethExecutive.cpp index 6e383d945..b5c6cc372 100644 --- a/libhistoric/AlethExecutive.cpp +++ b/libhistoric/AlethExecutive.cpp @@ -12,6 +12,7 @@ #include "libethereum/Interface.h" #include "libevm/LegacyVM.h" #include "libevm/VMFactory.h" +#include #include using namespace std; @@ -75,10 +76,11 @@ void AlethExecutive::accrueSubState( SubState& _parentContext ) { void AlethExecutive::initialize( Transaction const& _transaction ) { m_t = _transaction; - m_baseGasRequired = m_t.baseGasRequired( m_sealEngine.evmSchedule( m_envInfo.number() ) ); + m_baseGasRequired = m_t.baseGasRequired( + m_chainParams.makeEvmSchedule( m_envInfo.committedBlockTimestamp(), m_envInfo.number() ) ); try { - m_sealEngine.verifyTransaction( - ImportRequirements::Everything, m_t, m_envInfo.header(), m_envInfo.gasUsed() ); + Ethash::verifyTransaction( m_chainParams, ImportRequirements::Everything, m_t, + m_envInfo.committedBlockTimestamp(), m_envInfo.header(), m_envInfo.gasUsed() ); } catch ( Exception const& ex ) { m_excepted = toTransactionException( ex ); throw; @@ -150,14 +152,14 @@ bool AlethExecutive::call( // for the transaction. // Increment associated nonce for sender. if ( _p.senderAddress != MaxAddress || - m_envInfo.number() < m_sealEngine.chainParams().experimentalForkBlock ) // EIP86 + m_envInfo.number() < m_chainParams.experimentalForkBlock ) // EIP86 m_s.incNonce( _p.senderAddress ); } m_savepoint = m_s.savepoint(); - if ( m_sealEngine.isPrecompiled( _p.codeAddress, m_envInfo.number() ) ) { + if ( m_chainParams.isPrecompiled( _p.codeAddress, m_envInfo.number() ) ) { // Empty RIPEMD contract needs to be deleted even in case of OOG // because of the anomaly on the main net caused by buggy behavior by both Geth and Parity // https://github.com/ethereum/go-ethereum/pull/3341/files#diff-2433aa143ee4772026454b8abd76b9dd @@ -168,7 +170,7 @@ bool AlethExecutive::call( if ( _p.receiveAddress == c_RipemdPrecompiledAddress ) m_s.unrevertableTouch( _p.codeAddress ); - bigint g = m_sealEngine.costOfPrecompiled( _p.codeAddress, _p.data, m_envInfo.number() ); + bigint g = m_chainParams.costOfPrecompiled( _p.codeAddress, _p.data, m_envInfo.number() ); if ( _p.gas < g ) { m_excepted = TransactionException::OutOfGasBase; // Bail from exception. @@ -179,7 +181,7 @@ bool AlethExecutive::call( bytes output; bool success; tie( success, output ) = - m_sealEngine.executePrecompiled( _p.codeAddress, _p.data, m_envInfo.number() ); + m_chainParams.executePrecompiled( _p.codeAddress, _p.data, m_envInfo.number() ); size_t outputSize = output.size(); m_output = owning_bytes_ref{ std::move( output ), 0, outputSize }; if ( !success ) { @@ -196,7 +198,7 @@ bool AlethExecutive::call( // Contract will be executed with the version stored in account auto const version = m_s.version( _p.codeAddress ); - m_ext = make_shared< AlethExtVM >( m_s, m_envInfo, m_sealEngine, _p.receiveAddress, + m_ext = make_shared< AlethExtVM >( m_s, m_envInfo, m_chainParams, _p.receiveAddress, _p.senderAddress, _origin, _p.apparentValue, _gasPrice, _p.data, &c, codeHash, version, m_depth, false, _p.staticCall ); } @@ -210,7 +212,9 @@ bool AlethExecutive::call( bool AlethExecutive::create( Address const& _txSender, u256 const& _endowment, u256 const& _gasPrice, u256 const& _gas, bytesConstRef _init, Address const& _origin ) { // Contract will be created with the version corresponding to latest hard fork - auto const latestVersion = m_sealEngine.evmSchedule( m_envInfo.number() ).accountVersion; + auto const latestVersion = + m_chainParams.makeEvmSchedule( m_envInfo.committedBlockTimestamp(), m_envInfo.number() ) + .accountVersion; return createWithAddressFromNonceAndSender( _txSender, _endowment, _gasPrice, _gas, _init, _origin, latestVersion ); } @@ -244,7 +248,7 @@ bool AlethExecutive::executeCreate( Address const& _sender, u256 const& _endowme u256 const& _gasPrice, u256 const& _gas, bytesConstRef _init, Address const& _origin, u256 const& _version ) { if ( _sender != MaxAddress || - m_envInfo.number() < m_sealEngine.chainParams().experimentalForkBlock ) // EIP86 + m_envInfo.number() < m_chainParams.experimentalForkBlock ) // EIP86 m_s.incNonce( _sender ); m_savepoint = m_s.savepoint(); @@ -271,7 +275,7 @@ bool AlethExecutive::executeCreate( Address const& _sender, u256 const& _endowme m_s.transferBalance( _sender, m_newAddress, _endowment ); u256 newNonce = m_s.requireAccountStartNonce(); - if ( m_envInfo.number() >= m_sealEngine.chainParams().EIP158ForkBlock ) + if ( m_envInfo.number() >= m_chainParams.EIP158ForkBlock ) newNonce += 1; m_s.setNonce( m_newAddress, newNonce ); @@ -279,7 +283,7 @@ bool AlethExecutive::executeCreate( Address const& _sender, u256 const& _endowme // Schedule _init execution if not empty. if ( !_init.empty() ) - m_ext = make_shared< AlethExtVM >( m_s, m_envInfo, m_sealEngine, m_newAddress, _sender, + m_ext = make_shared< AlethExtVM >( m_s, m_envInfo, m_chainParams, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, sha3( _init ), _version, m_depth, true, false ); else diff --git a/libhistoric/AlethExecutive.h b/libhistoric/AlethExecutive.h index 62f18c8b1..eb61ea2d1 100644 --- a/libhistoric/AlethExecutive.h +++ b/libhistoric/AlethExecutive.h @@ -47,8 +47,8 @@ class AlethExecutive { public: /// Simple constructor; executive will operate on given state, with the given environment info. AlethExecutive( dev::eth::HistoricState& _s, EnvInfo const& _envInfo, - SealEngineFace const& _sealEngine, unsigned _level = 0 ) - : m_s( _s ), m_envInfo( _envInfo ), m_depth( _level ), m_sealEngine( _sealEngine ){}; + ChainOperationParams const& _chainParams, unsigned _level = 0 ) + : m_s( _s ), m_envInfo( _envInfo ), m_depth( _level ), m_chainParams( _chainParams ){}; /** Easiest constructor. * Creates executive to operate on the state of end of the given block, populating environment @@ -167,7 +167,7 @@ class AlethExecutive { LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize(). u256 m_gasCost; - SealEngineFace const& m_sealEngine; + ChainOperationParams const& m_chainParams; bool m_isCreation = false; Address m_newAddress; diff --git a/libhistoric/AlethExtVM.cpp b/libhistoric/AlethExtVM.cpp index ab7f12199..0ffa58a8b 100644 --- a/libhistoric/AlethExtVM.cpp +++ b/libhistoric/AlethExtVM.cpp @@ -119,7 +119,7 @@ evmc_status_code AlethExtVM::transactionExceptionToEvmcStatusCode( TransactionEx CallResult AlethExtVM::call( CallParameters& _p ) { - dev::eth::AlethExecutive e{ m_s, envInfo(), m_sealEngine, depth + 1 }; + dev::eth::AlethExecutive e{ m_s, envInfo(), m_chainParams, depth + 1 }; if ( !e.call( _p, gasPrice, origin ) ) { go( depth, e, _p.onOp ); e.accrueSubState( sub ); @@ -143,7 +143,7 @@ void AlethExtVM::setStore( u256 _n, u256 _v ) { CreateResult AlethExtVM::create( u256 _endowment, u256& io_gas, bytesConstRef _code, Instruction _op, u256 _salt, OnOpFunc const& _onOp ) { - AlethExecutive e{ m_s, envInfo(), m_sealEngine, depth + 1 }; + AlethExecutive e{ m_s, envInfo(), m_chainParams, depth + 1 }; bool result = false; if ( _op == Instruction::CREATE ) result = e.createOpcode( myAddress, _endowment, gasPrice, io_gas, _code, origin ); @@ -177,7 +177,7 @@ h256 AlethExtVM::blockHash( u256 _number ) { if ( _number >= currentNumber || _number < ( std::max< u256 >( 256, currentNumber ) - 256 ) ) return h256(); - if ( currentNumber < m_sealEngine.chainParams().experimentalForkBlock + 256 ) { + if ( currentNumber < m_chainParams.experimentalForkBlock + 256 ) { h256 const parentHash = envInfo().header().parentHash(); h256s const lastHashes = envInfo().lastHashes().precedingHashes( parentHash ); @@ -192,6 +192,6 @@ h256 AlethExtVM::blockHash( u256 _number ) { ExecutionResult res; std::tie( res, std::ignore ) = - m_s.execute( envInfo(), m_sealEngine, tx, skale::Permanence::Reverted ); + m_s.execute( envInfo(), m_chainParams, tx, skale::Permanence::Reverted ); return h256( res.output ); } diff --git a/libhistoric/AlethExtVM.h b/libhistoric/AlethExtVM.h index 41eed8015..c7a3785d1 100644 --- a/libhistoric/AlethExtVM.h +++ b/libhistoric/AlethExtVM.h @@ -30,15 +30,17 @@ class SealEngineFace; class AlethExtVM : public ExtVMFace { public: /// Full constructor. - AlethExtVM( HistoricState& _s, EnvInfo const& _envInfo, SealEngineFace const& _sealEngine, - Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, - bytesConstRef _data, bytesConstRef _code, h256 const& _codeHash, u256 const& _version, - unsigned _depth, bool _isCreate, bool _staticCall ) + AlethExtVM( HistoricState& _s, EnvInfo const& _envInfo, + ChainOperationParams const& _chainParams, Address _myAddress, Address _caller, + Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, + h256 const& _codeHash, u256 const& _version, unsigned _depth, bool _isCreate, + bool _staticCall ) : ExtVMFace( _envInfo, _myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(), _codeHash, _version, _depth, _isCreate, _staticCall ), m_s( _s ), - m_sealEngine( _sealEngine ), - m_evmSchedule( initEvmSchedule( envInfo().number(), _version ) ) { + m_chainParams( _chainParams ), + m_evmSchedule( initEvmSchedule( + _envInfo.committedBlockTimestamp(), envInfo().number(), _version ) ) { // Contract: processing account must exist. In case of CALL, the ExtVM // is created only if an account has code (so exist). In case of CREATE // the account must be created first. @@ -97,10 +99,12 @@ class AlethExtVM : public ExtVMFace { static evmc_status_code transactionExceptionToEvmcStatusCode( TransactionException ex ); private: - EVMSchedule const& initEvmSchedule( int64_t _blockNumber, u256 const& _version ) const { + EVMSchedule initEvmSchedule( + time_t _committedBlockTimestamp, int64_t _workingBlockNumber, u256 const& _version ) const { // If _version is latest for the block, select corresponding latest schedule. // Otherwise run with the latest schedule known to correspond to the _version. - EVMSchedule const& currentBlockSchedule = m_sealEngine.evmSchedule( _blockNumber ); + EVMSchedule currentBlockSchedule = + m_chainParams.makeEvmSchedule( _committedBlockTimestamp, _workingBlockNumber ); if ( currentBlockSchedule.accountVersion == _version ) return currentBlockSchedule; else @@ -109,8 +113,9 @@ class AlethExtVM : public ExtVMFace { dev::eth::HistoricState& m_s; ///< A reference to the base state. - SealEngineFace const& m_sealEngine; - EVMSchedule const& m_evmSchedule; + time_t m_committedBlockTimestamp; + ChainOperationParams const& m_chainParams; + EVMSchedule const m_evmSchedule; }; } // namespace eth diff --git a/libhistoric/HistoricState.cpp b/libhistoric/HistoricState.cpp index 1e50521de..9f3938a71 100644 --- a/libhistoric/HistoricState.cpp +++ b/libhistoric/HistoricState.cpp @@ -596,11 +596,11 @@ void HistoricState::rollback( size_t _savepoint ) { } std::pair< ExecutionResult, TransactionReceipt > HistoricState::execute( EnvInfo const& _envInfo, - SealEngineFace const& _sealEngine, Transaction const& _t, skale::Permanence _p, + eth::ChainOperationParams const& _chainParams, Transaction const& _t, skale::Permanence _p, OnOpFunc const& _onOp ) { // Create and initialize the executive. This will throw fairly cheaply and quickly if the // transaction is bad in any way. - AlethExecutive e( *this, _envInfo, _sealEngine ); + AlethExecutive e( *this, _envInfo, _chainParams, 0 ); ExecutionResult res; e.setResultRecipient( res ); @@ -631,7 +631,7 @@ std::pair< ExecutionResult, TransactionReceipt > HistoricState::execute( EnvInfo } TransactionReceipt const receipt = - _envInfo.number() >= _sealEngine.chainParams().byzantiumForkBlock ? + _envInfo.number() >= _chainParams.byzantiumForkBlock ? TransactionReceipt( statusCode, startGasUsed + e.gasUsed(), e.logs() ) : TransactionReceipt( globalRoot(), startGasUsed + e.gasUsed(), e.logs() ); diff --git a/libhistoric/HistoricState.h b/libhistoric/HistoricState.h index f1273bb58..8c3a8cb4a 100644 --- a/libhistoric/HistoricState.h +++ b/libhistoric/HistoricState.h @@ -157,7 +157,7 @@ class HistoricState { /// Execute a given transaction. /// This will change the state accordingly. std::pair< ExecutionResult, TransactionReceipt > execute( EnvInfo const& _envInfo, - SealEngineFace const& _sealEngine, Transaction const& _t, + eth::ChainOperationParams const& _chainParams, Transaction const& _t, skale::Permanence _p = skale::Permanence::Committed, OnOpFunc const& _onOp = OnOpFunc() ); diff --git a/libskale/CMakeLists.txt b/libskale/CMakeLists.txt index 26b492aa1..f05274265 100644 --- a/libskale/CMakeLists.txt +++ b/libskale/CMakeLists.txt @@ -12,18 +12,9 @@ set(sources SnapshotHashAgent.cpp UnsafeRegion.cpp TotalStorageUsedPatch.cpp - ContractStorageLimitPatch.cpp - ContractStorageZeroValuePatch.cpp - VerifyDaSigsPatch.cpp AmsterdamFixPatch.cpp - RevertableFSPatch.cpp OverlayFS.cpp - StorageDestructionPatch.cpp - POWCheckPatch.cpp - PrecompiledConfigPatch.cpp - PushZeroPatch.cpp SkipInvalidTransactionsPatch.cpp - CorrectForkInPowPatch.cpp ) set(headers @@ -38,14 +29,9 @@ set(headers SnapshotHashAgent.h UnsafeRegion.h TotalStorageUsedPatch.h - ContractStorageLimitPatch.h AmsterdamFixPatch.h - RevertableFSPatch.h - POWCheckPatch.h - PrecompiledConfigPatch.h OverlayFS.h SkipInvalidTransactionsPatch.h - CorrectForkInPowPatch.h ) add_library(skale ${sources} ${headers}) diff --git a/libskale/ConsensusGasPricer.cpp b/libskale/ConsensusGasPricer.cpp index 5412cb4c3..930c1d2b4 100644 --- a/libskale/ConsensusGasPricer.cpp +++ b/libskale/ConsensusGasPricer.cpp @@ -9,6 +9,7 @@ dev::u256 ConsensusGasPricer::ask( dev::eth::Block const& ) const { return bid(); } -dev::u256 ConsensusGasPricer::bid( dev::eth::TransactionPriority /*_p*/ ) const { - return m_skaleHost.getGasPrice(); +dev::u256 ConsensusGasPricer::bid( + unsigned _blockNumber, dev::eth::TransactionPriority /*_p*/ ) const { + return m_skaleHost.getGasPrice( _blockNumber ); } diff --git a/libskale/ConsensusGasPricer.h b/libskale/ConsensusGasPricer.h index 93f351b31..181667b54 100644 --- a/libskale/ConsensusGasPricer.h +++ b/libskale/ConsensusGasPricer.h @@ -10,7 +10,7 @@ class ConsensusGasPricer : public dev::eth::GasPricer { ConsensusGasPricer( const SkaleHost& _host ); virtual dev::u256 ask( dev::eth::Block const& ) const override; - virtual dev::u256 bid( + virtual dev::u256 bid( unsigned _blockNumber = dev::eth::LatestBlock, dev::eth::TransactionPriority _p = dev::eth::TransactionPriority::Medium ) const override; private: diff --git a/libskale/ContractStorageLimitPatch.cpp b/libskale/ContractStorageLimitPatch.cpp deleted file mode 100644 index 3e15ef1cc..000000000 --- a/libskale/ContractStorageLimitPatch.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "ContractStorageLimitPatch.h" - -time_t ContractStorageLimitPatch::contractStoragePatchTimestamp; -time_t ContractStorageLimitPatch::lastBlockTimestamp; - -bool ContractStorageLimitPatch::isEnabled() { - if ( contractStoragePatchTimestamp == 0 ) { - return false; - } - return contractStoragePatchTimestamp <= lastBlockTimestamp; -} diff --git a/libskale/ContractStorageLimitPatch.h b/libskale/ContractStorageLimitPatch.h deleted file mode 100644 index f7442b572..000000000 --- a/libskale/ContractStorageLimitPatch.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef CONTRACTSTORAGELIMITPATCH_H -#define CONTRACTSTORAGELIMITPATCH_H - -#include - -#include - -namespace dev { -namespace eth { -class Client; -} -} // namespace dev - -/* - * Context: contractStorageUsed counter didn't work well in one case - * Solution: we fixed the bug and added new config field introudceChangesTimestamp - * Purpose: avoid incorrect txn behaviour - * Version introduced: - */ -class ContractStorageLimitPatch : public SchainPatch { -public: - static bool isEnabled(); - - static void setTimestamp( time_t _timeStamp ) { - printInfo( __FILE__, _timeStamp ); - contractStoragePatchTimestamp = _timeStamp; - } - -private: - friend class dev::eth::Client; - static time_t contractStoragePatchTimestamp; - static time_t lastBlockTimestamp; -}; - -#endif // CONTRACTSTORAGELIMITPATCH_H diff --git a/libskale/ContractStorageZeroValuePatch.cpp b/libskale/ContractStorageZeroValuePatch.cpp deleted file mode 100644 index b77887f74..000000000 --- a/libskale/ContractStorageZeroValuePatch.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "ContractStorageZeroValuePatch.h" - -time_t ContractStorageZeroValuePatch::contractStorageZeroValuePatchTimestamp = 0; -time_t ContractStorageZeroValuePatch::lastBlockTimestamp = 0; - -bool ContractStorageZeroValuePatch::isEnabled() { - if ( contractStorageZeroValuePatchTimestamp == 0 ) { - return false; - } - return contractStorageZeroValuePatchTimestamp <= lastBlockTimestamp; -} diff --git a/libskale/ContractStorageZeroValuePatch.h b/libskale/ContractStorageZeroValuePatch.h deleted file mode 100644 index f64d3059a..000000000 --- a/libskale/ContractStorageZeroValuePatch.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef CONTRACTSTORAGEZEROVALUEPATCH_H -#define CONTRACTSTORAGEZEROVALUEPATCH_H - -#include - -#include - -namespace dev { -namespace eth { -class Client; -} -} // namespace dev - -/* - * Context: contractStorageUsed counter didn't work well in one case - * Solution: we fixed the bug and added new config field introudceChangesTimestamp - * Purpose: avoid incorrect txn behaviour - * Version introduced: - */ -class ContractStorageZeroValuePatch : public SchainPatch { -public: - static bool isEnabled(); - - static void setTimestamp( time_t _timeStamp ) { - printInfo( __FILE__, _timeStamp ); - contractStorageZeroValuePatchTimestamp = _timeStamp; - } - - -private: - friend class dev::eth::Client; - static time_t contractStorageZeroValuePatchTimestamp; - static time_t lastBlockTimestamp; -}; - -#endif // CONTRACTSTORAGEZEROVALUYEPATCH_H diff --git a/libskale/CorrectForkInPowPatch.cpp b/libskale/CorrectForkInPowPatch.cpp deleted file mode 100644 index aec18bc29..000000000 --- a/libskale/CorrectForkInPowPatch.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#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 deleted file mode 100644 index 94c0f766e..000000000 --- a/libskale/CorrectForkInPowPatch.h +++ /dev/null @@ -1,41 +0,0 @@ -#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/libskale/OverlayDB.cpp b/libskale/OverlayDB.cpp index 2c6b5eebb..113cb9987 100644 --- a/libskale/OverlayDB.cpp +++ b/libskale/OverlayDB.cpp @@ -23,8 +23,8 @@ */ #include "OverlayDB.h" -#include "ContractStorageZeroValuePatch.h" #include "libhistoric/HistoricState.h" +#include #include @@ -151,7 +151,7 @@ void OverlayDB::commitStorageValues() { static const h256 ZERO_VALUE( 0 ); - if ( ContractStorageZeroValuePatch::isEnabled() && value == ZERO_VALUE ) { + if ( ContractStorageZeroValuePatch::isEnabledInWorkingBlock() && value == ZERO_VALUE ) { // if the value is zero, the pair will be deleted in LevelDB // if it exists m_db_face->kill( diff --git a/libskale/OverlayFS.cpp b/libskale/OverlayFS.cpp index 8796dff4d..d9867249e 100644 --- a/libskale/OverlayFS.cpp +++ b/libskale/OverlayFS.cpp @@ -23,8 +23,8 @@ */ #include "OverlayFS.h" -#include "RevertableFSPatch.h" #include +#include #include #include diff --git a/libskale/POWCheckPatch.cpp b/libskale/POWCheckPatch.cpp deleted file mode 100644 index 79a43dddd..000000000 --- a/libskale/POWCheckPatch.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "POWCheckPatch.h" - -time_t POWCheckPatch::powCheckPatchTimestamp; -time_t POWCheckPatch::lastBlockTimestamp; - -bool POWCheckPatch::isEnabled() { - if ( powCheckPatchTimestamp == 0 ) { - return false; - } - return powCheckPatchTimestamp <= lastBlockTimestamp; -} diff --git a/libskale/POWCheckPatch.h b/libskale/POWCheckPatch.h deleted file mode 100644 index 65a4f5905..000000000 --- a/libskale/POWCheckPatch.h +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include - -#ifndef POWCHECKPATCH_H -#define POWCHECKPATCH_H - -namespace dev { -namespace eth { -class Client; -} -} // namespace dev - -/* - * Context: enable fix for POW txns gas limit check - */ -class POWCheckPatch : public SchainPatch { -public: - static bool isEnabled(); - - static void setTimestamp( time_t _timeStamp ) { - printInfo( __FILE__, _timeStamp ); - powCheckPatchTimestamp = _timeStamp; - } - -private: - friend class dev::eth::Client; - static time_t powCheckPatchTimestamp; - static time_t lastBlockTimestamp; -}; - -#endif // POWCHECKPATCH_H diff --git a/libskale/PrecompiledConfigPatch.cpp b/libskale/PrecompiledConfigPatch.cpp deleted file mode 100644 index f36557d61..000000000 --- a/libskale/PrecompiledConfigPatch.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "PrecompiledConfigPatch.h" - -time_t PrecompiledConfigPatch::precompiledConfigPatchTimestamp; -time_t PrecompiledConfigPatch::lastBlockTimestamp; - -bool PrecompiledConfigPatch::isEnabled() { - if ( precompiledConfigPatchTimestamp == 0 ) { - return false; - } - return precompiledConfigPatchTimestamp <= lastBlockTimestamp; -} diff --git a/libskale/PrecompiledConfigPatch.h b/libskale/PrecompiledConfigPatch.h deleted file mode 100644 index 776dd47cc..000000000 --- a/libskale/PrecompiledConfigPatch.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef PRECOMPILEDCONFIGPATCH_H -#define PRECOMPILEDCONFIGPATCH_H - -#include -#include - -namespace dev { -namespace eth { -class Client; -} -} // namespace dev - -/* - * Context: enable precompiled contracts to read historical config data - */ -class PrecompiledConfigPatch : public SchainPatch { -public: - static bool isEnabled(); - - static void setTimestamp( time_t _timeStamp ) { - printInfo( __FILE__, _timeStamp ); - precompiledConfigPatchTimestamp = _timeStamp; - } - -private: - friend class dev::eth::Client; - static time_t precompiledConfigPatchTimestamp; - static time_t lastBlockTimestamp; -}; - - -#endif // PRECOMPILEDCONFIGPATCH_H diff --git a/libskale/PushZeroPatch.cpp b/libskale/PushZeroPatch.cpp deleted file mode 100644 index e7ef4f6c1..000000000 --- a/libskale/PushZeroPatch.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "PushZeroPatch.h" - -time_t PushZeroPatch::pushZeroPatchTimestamp; -time_t PushZeroPatch::lastBlockTimestamp; - -bool PushZeroPatch::isEnabled() { - if ( pushZeroPatchTimestamp == 0 ) { - return false; - } - return pushZeroPatchTimestamp <= lastBlockTimestamp; -} diff --git a/libskale/PushZeroPatch.h b/libskale/PushZeroPatch.h deleted file mode 100644 index 6ebab4e9f..000000000 --- a/libskale/PushZeroPatch.h +++ /dev/null @@ -1,27 +0,0 @@ -#include -#include - -namespace dev { -namespace eth { -class Client; -} -} // namespace dev - -/* - * Context: enable effective storage destruction - */ -class PushZeroPatch : public SchainPatch { -public: - static bool isEnabled(); - - static void setTimestamp( time_t _timeStamp ) { - printInfo( __FILE__, _timeStamp ); - pushZeroPatchTimestamp = _timeStamp; - } - - -private: - friend class dev::eth::Client; - static time_t pushZeroPatchTimestamp; - static time_t lastBlockTimestamp; -}; \ No newline at end of file diff --git a/libskale/RevertableFSPatch.cpp b/libskale/RevertableFSPatch.cpp deleted file mode 100644 index 3f638befd..000000000 --- a/libskale/RevertableFSPatch.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "RevertableFSPatch.h" - -time_t RevertableFSPatch::revertableFSPatchTimestamp; -time_t RevertableFSPatch::lastBlockTimestamp; - -bool RevertableFSPatch::isEnabled() { - if ( revertableFSPatchTimestamp == 0 ) { - return false; - } - return revertableFSPatchTimestamp <= lastBlockTimestamp; -} \ No newline at end of file diff --git a/libskale/RevertableFSPatch.h b/libskale/RevertableFSPatch.h deleted file mode 100644 index 83321bfec..000000000 --- a/libskale/RevertableFSPatch.h +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include - -namespace dev { -namespace eth { -class Client; -} -} // namespace dev - -/* - * Context: enable revertable filestorage precompileds - */ -class RevertableFSPatch : public SchainPatch { -public: - static bool isEnabled(); - - static void setTimestamp( time_t _timeStamp ) { - printInfo( __FILE__, _timeStamp ); - revertableFSPatchTimestamp = _timeStamp; - } - -private: - friend class dev::eth::Client; - static time_t revertableFSPatchTimestamp; - static time_t lastBlockTimestamp; -}; \ No newline at end of file diff --git a/libskale/SkipInvalidTransactionsPatch.cpp b/libskale/SkipInvalidTransactionsPatch.cpp index 5ab660337..571f1bc96 100644 --- a/libskale/SkipInvalidTransactionsPatch.cpp +++ b/libskale/SkipInvalidTransactionsPatch.cpp @@ -2,11 +2,21 @@ using namespace dev::eth; -time_t SkipInvalidTransactionsPatch::activationTimestamp; -time_t SkipInvalidTransactionsPatch::lastBlockTimestamp; +bool SkipInvalidTransactionsPatch::hasPotentialInvalidTransactionsInBlock( + dev::eth::BlockNumber _bn, const dev::eth::BlockChain& _bc ) { + if ( _bn == 0 ) + return false; + + time_t activationTimestamp = _bc.chainParams().getPatchTimestamp( getEnum() ); -bool SkipInvalidTransactionsPatch::isEnabled() { if ( activationTimestamp == 0 ) - return false; - return lastBlockTimestamp >= activationTimestamp; + return true; + + if ( _bn == dev::eth::PendingBlock ) + return !isEnabledInWorkingBlock(); + + if ( _bn == dev::eth::LatestBlock ) + _bn = _bc.number(); + + return !isEnabledInBlock( _bc, _bn ); } diff --git a/libskale/SkipInvalidTransactionsPatch.h b/libskale/SkipInvalidTransactionsPatch.h index 7c171b09e..558c9261b 100644 --- a/libskale/SkipInvalidTransactionsPatch.h +++ b/libskale/SkipInvalidTransactionsPatch.h @@ -2,7 +2,7 @@ #define SKIPINVALIDTRANSACTIONSPATCH_H #include -#include +#include #include #include #include @@ -32,21 +32,24 @@ class Client; // Transactions are removed from Transaction Queue as usually. // TODO better start to apply patches from 1st block after timestamp, not second + class SkipInvalidTransactionsPatch : public SchainPatch { public: - static bool isEnabled(); - - static void setTimestamp( time_t _activationTimestamp ) { - activationTimestamp = _activationTimestamp; - printInfo( __FILE__, _activationTimestamp ); + static SchainPatchEnum getEnum() { return SchainPatchEnum::SkipInvalidTransactionsPatch; } + static bool isEnabledInWorkingBlock() { return isPatchEnabledInWorkingBlock( getEnum() ); } + static bool isEnabledWhen( time_t _committedBlockTimestamp ) { + return isPatchEnabledWhen( getEnum(), _committedBlockTimestamp ); } - - static time_t getActivationTimestamp() { return activationTimestamp; } - -private: - friend class dev::eth::Client; - static time_t activationTimestamp; - static time_t lastBlockTimestamp; + static bool isEnabledInBlock( + const dev::eth::BlockChain& _bc, dev::eth::BlockNumber _bn = dev::eth::PendingBlock ) { + time_t timestamp = _bc.chainParams().getPatchTimestamp( getEnum() ); + return _bc.isPatchTimestampActiveInBlockNumber( timestamp, _bn ); + } + // returns true if block N can contain invalid transactions + // returns false if this block was created with SkipInvalidTransactionsPatch and they were + // skipped + static bool hasPotentialInvalidTransactionsInBlock( + dev::eth::BlockNumber _bn, const dev::eth::BlockChain& _bc ); }; #endif // SKIPINVALIDTRANSACTIONSPATCH_H diff --git a/libskale/State.cpp b/libskale/State.cpp index 3e3526610..405eb2c69 100644 --- a/libskale/State.cpp +++ b/libskale/State.cpp @@ -36,8 +36,6 @@ #include #include -#include "ContractStorageLimitPatch.h" - #include "libweb3jsonrpc/Eth.h" #include "libweb3jsonrpc/JsonHelper.h" @@ -45,8 +43,7 @@ #include #include -#include -#include +#include namespace fs = boost::filesystem; @@ -359,7 +356,7 @@ std::unordered_map< Address, u256 > State::addresses() const { boost::shared_lock< boost::shared_mutex > lock( *x_db_ptr ); if ( !checkVersion() ) { cerror << "Current state version is " << m_currentVersion << " but stored version is " - << *m_storedVersion << endl; + << *m_storedVersion; BOOST_THROW_EXCEPTION( AttemptToReadFromStateInThePast() ); } @@ -441,7 +438,7 @@ eth::Account* State::account( Address const& _address ) { if ( !checkVersion() ) { cerror << "Current state version is " << m_currentVersion << " but stored version is " - << *m_storedVersion << endl; + << *m_storedVersion; BOOST_THROW_EXCEPTION( AttemptToReadFromStateInThePast() ); } @@ -510,7 +507,7 @@ void State::commit( dev::eth::CommitBehaviour _commitBehaviour ) { m_db_ptr->kill( address ); m_db_ptr->killAuxiliary( address, Auxiliary::CODE ); - if ( StorageDestructionPatch::isEnabled() ) { + if ( StorageDestructionPatch::isEnabledInWorkingBlock() ) { clearStorage( address ); } @@ -669,7 +666,7 @@ std::map< h256, std::pair< u256, u256 > > State::storage_WITHOUT_LOCK( const Address& _contract ) const { if ( !checkVersion() ) { cerror << "Current state version is " << m_currentVersion << " but stored version is " - << *m_storedVersion << endl; + << *m_storedVersion; BOOST_THROW_EXCEPTION( AttemptToReadFromStateInThePast() ); } @@ -689,7 +686,7 @@ std::map< h256, std::pair< u256, u256 > > State::storage_WITHOUT_LOCK( } } - cdebug << "Self-destruct cleared values:" << storage.size() << endl; + cdebug << "Self-destruct cleared values:" << storage.size(); return storage; } @@ -896,7 +893,7 @@ void State::rollback( size_t _savepoint ) { // change log entry. switch ( change.kind ) { case Change::Storage: - if ( ContractStorageLimitPatch::isEnabled() ) { + if ( ContractStoragePatch::isEnabledInWorkingBlock() ) { rollbackStorageChange( change, account ); } else { account.setStorage( change.key, change.value ); @@ -925,7 +922,7 @@ void State::rollback( size_t _savepoint ) { m_changeLog.pop_back(); } clearFileStorageCache(); - if ( !ContractStorageLimitPatch::isEnabled() ) { + if ( !ContractStoragePatch::isEnabledInWorkingBlock() ) { resetStorageChanges(); } } @@ -1008,16 +1005,17 @@ bool State::empty() const { } std::pair< ExecutionResult, TransactionReceipt > State::execute( EnvInfo const& _envInfo, - SealEngineFace const& _sealEngine, Transaction const& _t, Permanence _p, + eth::ChainOperationParams const& _chainParams, Transaction const& _t, Permanence _p, OnOpFunc const& _onOp ) { // Create and initialize the executive. This will throw fairly cheaply and quickly if the // transaction is bad in any way. // HACK 0 here is for gasPrice - Executive e( *this, _envInfo, _sealEngine, 0, 0, _p != Permanence::Committed ); + // TODO Not sure that 1st 0 as timestamp is acceptable here + Executive e( *this, _envInfo, _chainParams, 0, 0, _p != Permanence::Committed ); ExecutionResult res; e.setResultRecipient( res ); - bool isCacheEnabled = RevertableFSPatch::isEnabled(); + bool isCacheEnabled = RevertableFSPatch::isEnabledWhen( _envInfo.committedBlockTimestamp() ); resetOverlayFS( isCacheEnabled ); auto onOp = _onOp; @@ -1058,14 +1056,14 @@ std::pair< ExecutionResult, TransactionReceipt > State::execute( EnvInfo const& // shaLastTx.hex() << "\n"; TransactionReceipt receipt = - _envInfo.number() >= _sealEngine.chainParams().byzantiumForkBlock ? + _envInfo.number() >= _chainParams.byzantiumForkBlock ? TransactionReceipt( statusCode, startGasUsed + e.gasUsed(), e.logs() ) : TransactionReceipt( EmptyTrie, startGasUsed + e.gasUsed(), e.logs() ); receipt.setRevertReason( strRevertReason ); m_db_ptr->addReceiptToPartials( receipt ); m_fs_ptr->commit(); - removeEmptyAccounts = _envInfo.number() >= _sealEngine.chainParams().EIP158ForkBlock; + removeEmptyAccounts = _envInfo.number() >= _chainParams.EIP158ForkBlock; commit( removeEmptyAccounts ? dev::eth::CommitBehaviour::RemoveEmptyAccounts : dev::eth::CommitBehaviour::KeepEmptyAccounts ); @@ -1077,7 +1075,7 @@ std::pair< ExecutionResult, TransactionReceipt > State::execute( EnvInfo const& } TransactionReceipt receipt = - _envInfo.number() >= _sealEngine.chainParams().byzantiumForkBlock ? + _envInfo.number() >= _chainParams.byzantiumForkBlock ? TransactionReceipt( statusCode, startGasUsed + e.gasUsed(), e.logs() ) : TransactionReceipt( EmptyTrie, startGasUsed + e.gasUsed(), e.logs() ); receipt.setRevertReason( strRevertReason ); diff --git a/libskale/State.h b/libskale/State.h index 0fe41329e..94c7118ea 100644 --- a/libskale/State.h +++ b/libskale/State.h @@ -55,7 +55,6 @@ struct hash< boost::filesystem::path > { namespace dev { namespace eth { -class SealEngineFace; class Executive; } // namespace eth } // namespace dev @@ -339,7 +338,7 @@ class State { /// Execute a given transaction. /// This will change the state accordingly. std::pair< dev::eth::ExecutionResult, dev::eth::TransactionReceipt > execute( - dev::eth::EnvInfo const& _envInfo, dev::eth::SealEngineFace const& _sealEngine, + dev::eth::EnvInfo const& _envInfo, dev::eth::ChainOperationParams const& _chainParams, dev::eth::Transaction const& _t, Permanence _p = Permanence::Committed, dev::eth::OnOpFunc const& _onOp = dev::eth::OnOpFunc() ); diff --git a/libskale/StorageDestructionPatch.cpp b/libskale/StorageDestructionPatch.cpp deleted file mode 100644 index 5ae6b03c7..000000000 --- a/libskale/StorageDestructionPatch.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "StorageDestructionPatch.h" - -time_t StorageDestructionPatch::storageDestructionPatchTimestamp; -time_t StorageDestructionPatch::lastBlockTimestamp; - -bool StorageDestructionPatch::isEnabled() { - if ( storageDestructionPatchTimestamp == 0 ) { - return false; - } - return storageDestructionPatchTimestamp <= lastBlockTimestamp; -} diff --git a/libskale/StorageDestructionPatch.h b/libskale/StorageDestructionPatch.h deleted file mode 100644 index fb16bbb4d..000000000 --- a/libskale/StorageDestructionPatch.h +++ /dev/null @@ -1,27 +0,0 @@ -#include -#include - -namespace dev { -namespace eth { -class Client; -} -} // namespace dev - -/* - * Context: enable effective storage destruction - */ -class StorageDestructionPatch : public SchainPatch { -public: - static bool isEnabled(); - - static void setTimestamp( time_t _timeStamp ) { - printInfo( __FILE__, _timeStamp ); - storageDestructionPatchTimestamp = _timeStamp; - } - - -private: - friend class dev::eth::Client; - static time_t storageDestructionPatchTimestamp; - static time_t lastBlockTimestamp; -}; \ No newline at end of file diff --git a/libskale/TotalStorageUsedPatch.cpp b/libskale/TotalStorageUsedPatch.cpp index f9438d09d..81a100655 100644 --- a/libskale/TotalStorageUsedPatch.cpp +++ b/libskale/TotalStorageUsedPatch.cpp @@ -8,12 +8,12 @@ using namespace dev; using namespace dev::eth; const Address magicAddress( toAddress( "0xE8E4Ea98530Bfe86f841E258fd6F3FD5c210c68f" ) ); -dev::eth::Client* TotalStorageUsedPatch::g_client; +dev::eth::Client* TotalStorageUsedPatch::client; void TotalStorageUsedPatch::onProgress( batched_io::db_operations_face& _db, size_t _blockNumber ) { if ( !_db.exists( dev::db::Slice( "\x0totalStorageUsed", 17 ) ) ) return; - if ( g_client->countAt( magicAddress ) == 0 ) + if ( client->countAt( magicAddress ) == 0 ) _db.insert( dev::db::Slice( "\x0totalStorageUsed", 17 ), dev::db::Slice( std::to_string( _blockNumber * 32 ) ) ); else diff --git a/libskale/TotalStorageUsedPatch.h b/libskale/TotalStorageUsedPatch.h index a15b5576b..40344833e 100644 --- a/libskale/TotalStorageUsedPatch.h +++ b/libskale/TotalStorageUsedPatch.h @@ -22,6 +22,10 @@ class Client; */ class TotalStorageUsedPatch : public SchainPatch { public: + static void init( dev::eth::Client* _client ) { + assert( _client ); + client = _client; + } static bool isInitOnChainNeeded( batched_io::db_operations_face& _db ) { return !_db.exists( ( dev::db::Slice ) "pieceUsageBytes" ); } @@ -32,8 +36,7 @@ class TotalStorageUsedPatch : public SchainPatch { static void onProgress( batched_io::db_operations_face& _db, size_t _blockNumber ); private: - friend class dev::eth::Client; - static dev::eth::Client* g_client; + static dev::eth::Client* client; }; #endif // TOTALSTORAGEUSEDPATCH_H diff --git a/libskale/VerifyDaSigsPatch.cpp b/libskale/VerifyDaSigsPatch.cpp deleted file mode 100644 index c8cf7a905..000000000 --- a/libskale/VerifyDaSigsPatch.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "VerifyDaSigsPatch.h" - -time_t VerifyDaSigsPatch::verifyDaSigsPatchTimestamp = 0; -time_t VerifyDaSigsPatch::lastBlockTimestamp = 0; - -bool VerifyDaSigsPatch::isEnabled() { - if ( verifyDaSigsPatchTimestamp == 0 ) { - return false; - } - return verifyDaSigsPatchTimestamp <= lastBlockTimestamp; -} -time_t VerifyDaSigsPatch::getVerifyDaSigsPatchTimestamp() { - return verifyDaSigsPatchTimestamp; -} diff --git a/libskale/VerifyDaSigsPatch.h b/libskale/VerifyDaSigsPatch.h deleted file mode 100644 index 426412507..000000000 --- a/libskale/VerifyDaSigsPatch.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef VERIFYDASIGSPATCH_H -#define VERIFYDASIDSPATCH_H - -#include - -#include - -namespace dev { -namespace eth { -class Client; -} -} // namespace dev - -/* - * Context: contractStorageUsed counter didn't work well in one case - * Solution: we fixed the bug and added new config field introudceChangesTimestamp - * Purpose: avoid incorrect txn behaviour - * Version introduced: - */ -class VerifyDaSigsPatch : public SchainPatch { -public: - static bool isEnabled(); - -private: - friend class dev::eth::Client; - static time_t verifyDaSigsPatchTimestamp; - static time_t lastBlockTimestamp; - - static void setTimestamp( time_t _timeStamp ) { - printInfo( __FILE__, _timeStamp ); - verifyDaSigsPatchTimestamp = _timeStamp; - } - -public: - static time_t getVerifyDaSigsPatchTimestamp(); -}; - -#endif \ No newline at end of file diff --git a/libskale/broadcaster.cpp b/libskale/broadcaster.cpp index 3b971c2aa..195b055f9 100644 --- a/libskale/broadcaster.cpp +++ b/libskale/broadcaster.cpp @@ -97,10 +97,8 @@ void* ZmqBroadcaster::server_socket() const { val = 60000; zmq_setsockopt( m_zmq_server_socket, ZMQ_HEARTBEAT_TTL, &val, sizeof( val ) ); - - val = 16; - zmq_setsockopt( m_zmq_server_socket, ZMQ_RCVHWM, &val, sizeof( val ) ); - val = 16; + // remove limits to prevent txns from being dropped out + val = 0; zmq_setsockopt( m_zmq_server_socket, ZMQ_SNDHWM, &val, sizeof( val ) ); @@ -134,11 +132,8 @@ void* ZmqBroadcaster::client_socket() const { value = 300; zmq_setsockopt( m_zmq_client_socket, ZMQ_TCP_KEEPALIVE_INTVL, &value, sizeof( value ) ); - value = 16; + value = 0; zmq_setsockopt( m_zmq_client_socket, ZMQ_RCVHWM, &value, sizeof( value ) ); - value = 16; - zmq_setsockopt( m_zmq_client_socket, ZMQ_SNDHWM, &value, sizeof( value ) ); - const dev::eth::ChainParams& ch = m_client.chainParams(); @@ -254,6 +249,8 @@ void ZmqBroadcaster::broadcast( const std::string& _rlp ) { int res = zmq_send( server_socket(), const_cast< char* >( _rlp.c_str() ), _rlp.size(), 0 ); if ( res <= 0 ) { + clog( dev::VerbosityWarning, "zmq-broadcaster" ) + << "Got error " << res << " in zmq_send: " << zmq_strerror( res ); throw std::runtime_error( "Zmq can't send data" ); } } diff --git a/libskale/httpserveroverride.cpp b/libskale/httpserveroverride.cpp index f76c94c69..be29eecef 100644 --- a/libskale/httpserveroverride.cpp +++ b/libskale/httpserveroverride.cpp @@ -2607,12 +2607,14 @@ bool SkaleServerOverride::implStartListening( // proxygen HTTP cc::success( "/" ) + cc::notice( esm2str( esm ) ) + " " ); return true; } catch ( const std::exception& ex ) { - logTraceServerEvent( false, ipVer, bIsSSL ? "HTTPS" : "HTTP", pSrv->serverIndex(), esm, + logTraceServerEvent( false, ipVer, bIsSSL ? "HTTPS" : "HTTP", + pSrv ? pSrv->serverIndex() : -1, esm, cc::fatal( "FAILED" ) + cc::error( " to start " ) + cc::attention( "proxygen" ) + cc::debug( "/" ) + cc::warn( bIsSSL ? "HTTPS" : "HTTP" ) + cc::error( " server: " ) + cc::warn( ex.what() ) ); } catch ( ... ) { - logTraceServerEvent( false, ipVer, bIsSSL ? "HTTPS" : "HTTP", pSrv->serverIndex(), esm, + logTraceServerEvent( false, ipVer, bIsSSL ? "HTTPS" : "HTTP", + pSrv ? pSrv->serverIndex() : -1, esm, cc::fatal( "FAILED" ) + cc::error( " to start " ) + cc::attention( "proxygen" ) + cc::debug( "/" ) + cc::warn( bIsSSL ? "HTTPS" : "HTTP" ) + cc::error( " server: " ) + cc::warn( "unknown exception" ) ); 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 dad59ce88..11097c4fd 100644 --- a/libweb3jsonrpc/Eth.cpp +++ b/libweb3jsonrpc/Eth.cpp @@ -48,6 +48,7 @@ using namespace dev::rpc; const uint64_t MAX_CALL_CACHE_ENTRIES = 1024; const uint64_t MAX_RECEIPT_CACHE_ENTRIES = 1024; +const u256 MAX_BLOCK_RANGE = 1024; #ifdef HISTORIC_STATE @@ -110,29 +111,9 @@ void GappedTransactionIndexCache::ensureCached( BlockNumber _bn, } // for } -// returns true if block N can contain invalid transactions -// returns false if this block was created with SkipInvalidTransactionsPatch and they were skipped -bool hasPotentialInvalidTransactionsInBlock( BlockNumber _bn, const Interface& _client ) { - if ( _bn == 0 ) - return false; - - if ( SkipInvalidTransactionsPatch::getActivationTimestamp() == 0 ) - return true; - - if ( _bn == PendingBlock ) - return !SkipInvalidTransactionsPatch::isEnabled(); - - if ( _bn == LatestBlock ) - _bn = _client.number(); - - time_t prev_ts = _client.blockInfo( _bn - 1 ).timestamp(); - - return prev_ts < SkipInvalidTransactionsPatch::getActivationTimestamp(); -} - #endif -Eth::Eth( const std::string& configPath, eth::Interface& _eth, eth::AccountHolder& _ethAccounts ) +Eth::Eth( const std::string& configPath, eth::Client& _eth, eth::AccountHolder& _ethAccounts ) : skutils::json_config_file_accessor( configPath ), m_eth( _eth ), m_ethAccounts( _ethAccounts ), @@ -294,7 +275,8 @@ Json::Value Eth::eth_getBlockTransactionCountByHash( string const& _blockHash ) #ifdef HISTORIC_STATE BlockNumber bn = client()->numberFromHash( blockHash ); - if ( !hasPotentialInvalidTransactionsInBlock( bn, *client() ) ) + if ( !SkipInvalidTransactionsPatch::hasPotentialInvalidTransactionsInBlock( + bn, client()->blockChain() ) ) #endif return toJS( client()->transactionCount( blockHash ) ); #ifdef HISTORIC_STATE @@ -313,7 +295,8 @@ Json::Value Eth::eth_getBlockTransactionCountByNumber( string const& _blockNumbe #ifdef HISTORIC_STATE BlockNumber bn = jsToBlockNumber( _blockNumber ); - if ( !hasPotentialInvalidTransactionsInBlock( bn, *client() ) ) + if ( !SkipInvalidTransactionsPatch::hasPotentialInvalidTransactionsInBlock( + bn, client()->blockChain() ) ) #endif return toJS( client()->transactionCount( jsToBlockNumber( _blockNumber ) ) ); #ifdef HISTORIC_STATE @@ -395,10 +378,8 @@ Json::Value Eth::eth_signTransaction( Json::Value const& _json ) { setTransactionDefaults( ts ); ts = client()->populateTransactionWithDefaults( ts ); pair< bool, Secret > ar = m_ethAccounts.authenticate( ts ); - Transaction t( ts, ar.second ); - RLPStream s; - t.streamRLP( s ); - return toJson( t, s.out() ); + Transaction t( ts, ar.second ); // always legacy, no prefix byte + return toJson( t, t.toBytes() ); } catch ( Exception const& ) { throw JsonRpcException( exceptionToErrorMessage() ); } @@ -430,8 +411,8 @@ Json::Value Eth::setSchainExitTime( Json::Value const& /*_transaction*/ ) { Json::Value Eth::eth_inspectTransaction( std::string const& _rlp ) { try { - return toJson( - Transaction( jsToBytes( _rlp, OnFailed::Throw ), CheckTransaction::Everything ) ); + return toJson( Transaction( jsToBytes( _rlp, OnFailed::Throw ), + CheckTransaction::Everything, EIP1559TransactionsPatch::isEnabledInWorkingBlock() ) ); } catch ( ... ) { BOOST_THROW_EXCEPTION( JsonRpcException( Errors::ERROR_RPC_INVALID_PARAMS ) ); } @@ -442,7 +423,8 @@ Json::Value Eth::eth_inspectTransaction( std::string const& _rlp ) { string Eth::eth_sendRawTransaction( std::string const& _rlp ) { // Don't need to check the transaction signature (CheckTransaction::None) since it // will be checked as a part of transaction import - Transaction t( jsToBytes( _rlp, OnFailed::Throw ), CheckTransaction::None ); + Transaction t( jsToBytes( _rlp, OnFailed::Throw ), CheckTransaction::None, false, + EIP1559TransactionsPatch::isEnabledInWorkingBlock() ); return toJS( client()->importTransaction( t ) ); } @@ -505,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 ); } @@ -556,12 +536,28 @@ Json::Value Eth::eth_getBlockByHash( string const& _blockHash, bool _includeTran if ( !client()->isKnown( h ) ) return Json::Value( Json::nullValue ); + u256 baseFeePerGas; + if ( EIP1559TransactionsPatch::isEnabledWhen( + client()->blockInfo( client()->numberFromHash( h ) - 1 ).timestamp() ) ) + try { + baseFeePerGas = client()->gasBidPrice( client()->numberFromHash( h ) - 1 ); + } catch ( std::invalid_argument& _e ) { + cdebug << "Cannot get gas price for block " << h; + cdebug << _e.what(); + // set default gasPrice + // probably the price was rotated out as we are asking the price for the old block + baseFeePerGas = client()->gasBidPrice(); + } + else + baseFeePerGas = 0; + if ( _includeTransactions ) { Transactions transactions = client()->transactions( h ); #ifdef HISTORIC_STATE BlockNumber bn = client()->numberFromHash( h ); - if ( hasPotentialInvalidTransactionsInBlock( bn, *client() ) ) { + if ( SkipInvalidTransactionsPatch::hasPotentialInvalidTransactionsInBlock( + bn, client()->blockChain() ) ) { // remove invalid transactions size_t index = 0; Transactions::iterator newEnd = std::remove_if( transactions.begin(), @@ -572,13 +568,14 @@ Json::Value Eth::eth_getBlockByHash( string const& _blockHash, bool _includeTran } #endif return toJson( client()->blockInfo( h ), client()->blockDetails( h ), - client()->uncleHashes( h ), transactions, client()->sealEngine() ); + client()->uncleHashes( h ), transactions, client()->sealEngine(), baseFeePerGas ); } else { h256s transactions = client()->transactionHashes( h ); #ifdef HISTORIC_STATE BlockNumber bn = client()->numberFromHash( h ); - if ( hasPotentialInvalidTransactionsInBlock( bn, *client() ) ) { + if ( SkipInvalidTransactionsPatch::hasPotentialInvalidTransactionsInBlock( + bn, client()->blockChain() ) ) { // remove invalid transactions size_t index = 0; h256s::iterator newEnd = std::remove_if( transactions.begin(), transactions.end(), @@ -589,7 +586,7 @@ Json::Value Eth::eth_getBlockByHash( string const& _blockHash, bool _includeTran } #endif return toJson( client()->blockInfo( h ), client()->blockDetails( h ), - client()->uncleHashes( h ), transactions, client()->sealEngine() ); + client()->uncleHashes( h ), transactions, client()->sealEngine(), baseFeePerGas ); } } catch ( ... ) { BOOST_THROW_EXCEPTION( JsonRpcException( Errors::ERROR_RPC_INVALID_PARAMS ) ); @@ -602,6 +599,22 @@ Json::Value Eth::eth_getBlockByNumber( string const& _blockNumber, bool _include if ( !client()->isKnown( h ) ) return Json::Value( Json::nullValue ); + BlockNumber bn = ( h == LatestBlock || h == PendingBlock ) ? client()->number() : h; + + u256 baseFeePerGas; + if ( EIP1559TransactionsPatch::isEnabledWhen( client()->blockInfo( bn - 1 ).timestamp() ) ) + try { + baseFeePerGas = client()->gasBidPrice( bn - 1 ); + } catch ( std::invalid_argument& _e ) { + cdebug << "Cannot get gas price for block " << bn; + cdebug << _e.what(); + // set default gasPrice + // probably the price was rotated out as we are asking the price for the old block + baseFeePerGas = client()->gasBidPrice(); + } + else + baseFeePerGas = 0; + #ifdef HISTORIC_STATE h256 bh = client()->hashFromNumber( h ); return eth_getBlockByHash( "0x" + bh.hex(), _includeTransactions ); @@ -611,11 +624,12 @@ Json::Value Eth::eth_getBlockByNumber( string const& _blockNumber, bool _include if ( _includeTransactions ) return toJson( client()->blockInfo( h ), client()->blockDetails( h ), - client()->uncleHashes( h ), client()->transactions( h ), client()->sealEngine() ); + client()->uncleHashes( h ), client()->transactions( h ), client()->sealEngine(), + baseFeePerGas ); else return toJson( client()->blockInfo( h ), client()->blockDetails( h ), client()->uncleHashes( h ), client()->transactionHashes( h ), - client()->sealEngine() ); + client()->sealEngine(), baseFeePerGas ); #endif } catch ( ... ) { BOOST_THROW_EXCEPTION( JsonRpcException( Errors::ERROR_RPC_INVALID_PARAMS ) ); @@ -649,7 +663,8 @@ Json::Value Eth::eth_getTransactionByBlockHashAndIndex( #ifdef HISTORIC_STATE BlockNumber bn = client()->numberFromHash( bh ); - if ( hasPotentialInvalidTransactionsInBlock( bn, *client() ) ) + if ( SkipInvalidTransactionsPatch::hasPotentialInvalidTransactionsInBlock( + bn, client()->blockChain() ) ) try { ti = m_gapCache->realIndexFromGapped( bn, ti ); } catch ( const out_of_range& ) { @@ -673,7 +688,8 @@ Json::Value Eth::eth_getTransactionByBlockNumberAndIndex( unsigned int ti = static_cast< unsigned int >( jsToInt( _transactionIndex ) ); #ifdef HISTORIC_STATE - if ( hasPotentialInvalidTransactionsInBlock( bn, *client() ) ) + if ( SkipInvalidTransactionsPatch::hasPotentialInvalidTransactionsInBlock( + bn, client()->blockChain() ) ) try { ti = m_gapCache->realIndexFromGapped( bn, ti ); } catch ( const out_of_range& ) { @@ -728,7 +744,8 @@ LocalisedTransactionReceipt Eth::eth_getTransactionReceipt( string const& _trans auto rcp = cli->localisedTransactionReceipt( h ); #ifdef HISTORIC_STATE - if ( hasPotentialInvalidTransactionsInBlock( rcp.blockNumber(), *client() ) ) { + if ( SkipInvalidTransactionsPatch::hasPotentialInvalidTransactionsInBlock( + rcp.blockNumber(), client()->blockChain() ) ) { // skip invalid if ( rcp.gasUsed() == 0 ) { m_receiptsCache.put( cacheKey, nullptr ); @@ -739,7 +756,8 @@ LocalisedTransactionReceipt Eth::eth_getTransactionReceipt( string const& _trans size_t newIndex = m_gapCache->gappedIndexFromReal( rcp.blockNumber(), rcp.transactionIndex() ); rcp = LocalisedTransactionReceipt( rcp, rcp.hash(), rcp.blockHash(), rcp.blockNumber(), - newIndex, rcp.from(), rcp.to(), rcp.gasUsed(), rcp.contractAddress() ); + newIndex, rcp.from(), rcp.to(), rcp.gasUsed(), rcp.contractAddress(), rcp.txType(), + rcp.effectiveGasPrice() ); } #endif @@ -833,6 +851,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 ) ); } @@ -849,7 +871,29 @@ Json::Value Eth::eth_getFilterLogs( string const& _filterId ) { Json::Value Eth::eth_getLogs( Json::Value const& _json ) { try { - return toJson( client()->logs( toLogFilter( _json ) ) ); + LogFilter filter = toLogFilter( _json ); + if ( !_json["blockHash"].isNull() ) { + if ( !_json["fromBlock"].isNull() || !_json["toBlock"].isNull() ) + BOOST_THROW_EXCEPTION( JsonRpcException( Errors::ERROR_RPC_INVALID_PARAMS, + "fromBlock and toBlock are not allowed if blockHash is present" ) ); + string strHash = _json["blockHash"].asString(); + if ( strHash.empty() ) + throw std::invalid_argument( "blockHash cannot be an empty string" ); + uint64_t number = m_eth.numberFromHash( jsToFixed< 32 >( strHash ) ); + if ( number == PendingBlock ) + BOOST_THROW_EXCEPTION( JsonRpcException( Errors::ERROR_RPC_INVALID_PARAMS, + "A block with this hash does not exist in the database. If this is an old " + "block, try connecting to an archive node" ) ); + filter.withEarliest( number ); + filter.withLatest( number ); + } + return toJson( client()->logs( filter ) ); + } 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 ( const JsonRpcException& ) { + throw; } catch ( ... ) { BOOST_THROW_EXCEPTION( JsonRpcException( Errors::ERROR_RPC_INVALID_PARAMS ) ); } @@ -901,6 +945,86 @@ string Eth::eth_chainId() { return toJS( client()->chainId() ); } +// SKALE ignores gas costs +// make response default, only fill in gasUsed field +Json::Value Eth::eth_createAccessList( + const Json::Value& _param1, const std::string& /*_param2*/ ) { + TransactionSkeleton t = toTransactionSkeleton( _param1 ); + setTransactionDefaults( t ); + + int64_t gas = static_cast< int64_t >( t.gas ); + auto executionResult = client()->estimateGas( t.from, t.value, t.to, t.data, gas, t.gasPrice ); + + auto result = Json::Value( Json::objectValue ); + result["accessList"] = Json::Value( Json::arrayValue ); + result["gasUsed"] = toJS( executionResult.first ); + + return result; +} + +Json::Value Eth::eth_feeHistory( const std::string& _blockCount, const std::string& _newestBlock, + const Json::Value& _rewardPercentiles ) { + try { + if ( !_rewardPercentiles.isArray() ) + throw std::runtime_error( "Reward percentiles must be a list" ); + + for ( auto p : _rewardPercentiles ) { + if ( !p.isUInt() || p > 100 ) { + throw std::runtime_error( "Percentiles must be positive integers less then 100" ); + } + } + + auto blockCount = jsToU256( _blockCount ); + if ( blockCount > MAX_BLOCK_RANGE ) + throw std::runtime_error( "Max block range reached. Please try smaller blockCount." ); + + auto newestBlock = jsToBlockNumber( _newestBlock ); + if ( newestBlock == dev::eth::LatestBlock ) + newestBlock = client()->number(); + + auto result = Json::Value( Json::objectValue ); + dev::u256 oldestBlock; + if ( blockCount > newestBlock ) + oldestBlock = 1; + else + oldestBlock = dev::u256( newestBlock ) - blockCount + 1; + result["oldestBlock"] = toJS( oldestBlock ); + + result["baseFeePerGas"] = Json::Value( Json::arrayValue ); + result["gasUsedRatio"] = Json::Value( Json::arrayValue ); + result["reward"] = Json::Value( Json::arrayValue ); + for ( auto bn = newestBlock; bn > oldestBlock - 1; --bn ) { + auto blockInfo = client()->blockInfo( bn - 1 ); + + if ( EIP1559TransactionsPatch::isEnabledWhen( blockInfo.timestamp() ) ) + result["baseFeePerGas"].append( toJS( client()->gasBidPrice( bn - 1 ) ) ); + else + result["baseFeePerGas"].append( toJS( 0 ) ); + + double gasUsedRatio = blockInfo.gasUsed().convert_to< double >() / + blockInfo.gasLimit().convert_to< double >(); + Json::Value gasUsedRatioObj = Json::Value( Json::realValue ); + gasUsedRatioObj = gasUsedRatio; + result["gasUsedRatio"].append( gasUsedRatioObj ); + + Json::Value reward = Json::Value( Json::arrayValue ); + reward.resize( _rewardPercentiles.size() ); + for ( Json::Value::ArrayIndex i = 0; i < reward.size(); ++i ) { + reward[i] = toJS( 0 ); + } + result["reward"].append( reward ); + } + + return result; + } catch ( ... ) { + BOOST_THROW_EXCEPTION( JsonRpcException( Errors::ERROR_RPC_INVALID_PARAMS ) ); + } +} + +std::string Eth::eth_maxPriorityFeePerGas() { + return "0x0"; +} + bool Eth::eth_submitWork( string const& _nonce, string const&, string const& _mixHash ) { try { return asEthashClient( client() ) diff --git a/libweb3jsonrpc/Eth.h b/libweb3jsonrpc/Eth.h index 8059860d7..2ecf0be5d 100644 --- a/libweb3jsonrpc/Eth.h +++ b/libweb3jsonrpc/Eth.h @@ -43,7 +43,7 @@ class KeyPair; namespace eth { class AccountHolder; struct TransactionSkeleton; -class Interface; +class Client; class LocalisedTransactionReceipt; } // namespace eth } // namespace dev @@ -57,7 +57,7 @@ namespace _detail { // cache for transaction index mapping class GappedTransactionIndexCache { public: - GappedTransactionIndexCache( size_t _cacheSize, const dev::eth::Interface& _client ) + GappedTransactionIndexCache( size_t _cacheSize, const dev::eth::Client& _client ) : client( _client ), cacheSize( _cacheSize ) { assert( _cacheSize > 0 ); } @@ -116,7 +116,7 @@ class GappedTransactionIndexCache { private: mutable std::shared_mutex mtx; - const dev::eth::Interface& client; + const dev::eth::Client& client; const size_t cacheSize; enum { UNDEFINED = ( size_t ) -1 }; @@ -135,7 +135,7 @@ std::string exceptionToErrorMessage(); */ class Eth : public dev::rpc::EthFace, public skutils::json_config_file_accessor { public: - Eth( const std::string& configPath, eth::Interface& _eth, eth::AccountHolder& _ethAccounts ); + Eth( const std::string& configPath, eth::Client& _eth, eth::AccountHolder& _ethAccounts ); virtual RPCModules implementedModules() const override { return RPCModules{ RPCModule{ "eth", "1.0" } }; @@ -216,13 +216,18 @@ class Eth : public dev::rpc::EthFace, public skutils::json_config_file_accessor virtual Json::Value eth_subscribe( Json::Value const& _transaction ) override; virtual Json::Value eth_unsubscribe( Json::Value const& _transaction ) override; virtual Json::Value setSchainExitTime( Json::Value const& _transaction ) override; + virtual Json::Value eth_createAccessList( + const Json::Value& param1, const std::string& param2 ) override; + virtual Json::Value eth_feeHistory( + const std::string& param1, const std::string& param2, const Json::Value& param3 ) override; + virtual std::string eth_maxPriorityFeePerGas() override; void setTransactionDefaults( eth::TransactionSkeleton& _t ); protected: - eth::Interface* client() { return &m_eth; } + eth::Client* client() { return &m_eth; } - eth::Interface& m_eth; + eth::Client& m_eth; eth::AccountHolder& m_ethAccounts; // a cache that maps the call request to the pair of response string and block number diff --git a/libweb3jsonrpc/EthFace.h b/libweb3jsonrpc/EthFace.h index b7bafedd3..c87edf57b 100644 --- a/libweb3jsonrpc/EthFace.h +++ b/libweb3jsonrpc/EthFace.h @@ -217,6 +217,17 @@ class EthFace : public ServerInterface< EthFace > { this->bindAndAddMethod( jsonrpc::Procedure( "eth_chainId", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL ), &dev::rpc::EthFace::eth_chainIdI ); + this->bindAndAddMethod( jsonrpc::Procedure( "eth_createAccessList", + jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1", + jsonrpc::JSON_OBJECT, "param2", jsonrpc::JSON_STRING, NULL ), + &dev::rpc::EthFace::eth_createAccessListI ); + this->bindAndAddMethod( jsonrpc::Procedure( "eth_feeHistory", jsonrpc::PARAMS_BY_POSITION, + jsonrpc::JSON_OBJECT, "param1", jsonrpc::JSON_STRING, "param2", + jsonrpc::JSON_STRING, "param3", jsonrpc::JSON_ARRAY, NULL ), + &dev::rpc::EthFace::eth_feeHistoryI ); + this->bindAndAddMethod( jsonrpc::Procedure( "eth_maxPriorityFeePerGas", + jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL ), + &dev::rpc::EthFace::eth_maxPriorityFeePerGasI ); } inline virtual void eth_protocolVersionI( const Json::Value& request, Json::Value& response ) { @@ -428,6 +439,24 @@ class EthFace : public ServerInterface< EthFace > { ( void ) request; response = this->eth_chainId(); } + inline virtual void eth_createAccessListI( const Json::Value& request, Json::Value& response ) { + if ( !request.isArray() || request.empty() ) + BOOST_THROW_EXCEPTION( + jsonrpc::JsonRpcException( jsonrpc::Errors::ERROR_RPC_INVALID_PARAMS ) ); + response = this->eth_createAccessList( request[0u], request[1u].asString() ); + } + inline virtual void eth_feeHistoryI( const Json::Value& request, Json::Value& response ) { + if ( !request.isArray() || request.size() != 3 ) + BOOST_THROW_EXCEPTION( + jsonrpc::JsonRpcException( jsonrpc::Errors::ERROR_RPC_INVALID_PARAMS ) ); + response = + this->eth_feeHistory( request[0u].asString(), request[1u].asString(), request[2u] ); + } + inline virtual void eth_maxPriorityFeePerGasI( + const Json::Value& request, Json::Value& response ) { + ( void ) request; + response = this->eth_maxPriorityFeePerGas(); + } virtual std::string eth_protocolVersion() = 0; virtual std::string eth_hashrate() = 0; virtual std::string eth_coinbase() = 0; @@ -493,6 +522,11 @@ class EthFace : public ServerInterface< EthFace > { virtual Json::Value eth_syncing() = 0; virtual std::string eth_estimateGas( const Json::Value& param1 ) = 0; virtual std::string eth_chainId() = 0; + virtual Json::Value eth_createAccessList( + const Json::Value& param1, const std::string& param2 ) = 0; + virtual Json::Value eth_feeHistory( + const std::string& param1, const std::string& param2, const Json::Value& param3 ) = 0; + virtual std::string eth_maxPriorityFeePerGas() = 0; }; } // namespace rpc diff --git a/libweb3jsonrpc/JsonHelper.cpp b/libweb3jsonrpc/JsonHelper.cpp index 883374885..b2828a1c2 100644 --- a/libweb3jsonrpc/JsonHelper.cpp +++ b/libweb3jsonrpc/JsonHelper.cpp @@ -95,7 +95,8 @@ Json::Value toJson( dev::eth::Transaction const& _t, std::pair< h256, unsigned > res["to"] = _t.isCreation() ? Json::Value() : toJS( _t.receiveAddress() ); res["from"] = toJS( _t.safeSender() ); res["gas"] = toJS( _t.gas() ); - res["gasPrice"] = toJS( _t.gasPrice() ); + if ( _t.txType() != dev::eth::TransactionType::Type2 ) + res["gasPrice"] = toJS( _t.gasPrice() ); res["nonce"] = toJS( _t.nonce() ); res["value"] = toJS( _t.value() ); res["blockHash"] = toJS( _location.first ); @@ -105,17 +106,38 @@ Json::Value toJson( dev::eth::Transaction const& _t, std::pair< h256, unsigned > toJS( 27 + _t.signature().v ); res["r"] = toJS( _t.signature().r ); res["s"] = toJS( _t.signature().s ); + res["type"] = toJS( int( _t.txType() ) ); + if ( _t.txType() != dev::eth::TransactionType::Legacy ) { + res["yParity"] = toJS( _t.signature().v ); + res["accessList"] = Json::Value( Json::arrayValue ); + for ( const auto& d : _t.accessList() ) { + auto list = RLP( d ); + Json::Value accessList; + accessList["address"] = dev::toHexPrefixed( list[0].toBytes() ); + accessList["storageKeys"] = Json::Value( Json::arrayValue ); + for ( const auto& k : list[1].toList() ) { + accessList["storageKeys"].append( dev::toHexPrefixed( k.toBytes() ) ); + } + res["accessList"].append( accessList ); + } + if ( _t.txType() != dev::eth::TransactionType::Type1 ) { + res["maxPriorityFeePerGas"] = toJS( _t.maxPriorityFeePerGas() ); + res["maxFeePerGas"] = toJS( _t.maxFeePerGas() ); + } + } } return res; } Json::Value toJson( dev::eth::BlockHeader const& _bi, BlockDetails const& _bd, - UncleHashes const& _us, Transactions const& _ts, SealEngineFace* _face ) { + UncleHashes const& _us, Transactions const& _ts, SealEngineFace* _face, u256 _gasPrice ) { Json::Value res = toJson( _bi, _face ); if ( _bi ) { res["totalDifficulty"] = toJS( _bd.totalDifficulty ); res["size"] = toJS( _bd.blockSizeBytes ); res["uncles"] = Json::Value( Json::arrayValue ); + if ( _gasPrice > 0 ) + res["baseFeePerGas"] = toJS( _gasPrice ); for ( h256 h : _us ) res["uncles"].append( toJS( h ) ); res["transactions"] = Json::Value( Json::arrayValue ); @@ -127,12 +149,14 @@ Json::Value toJson( dev::eth::BlockHeader const& _bi, BlockDetails const& _bd, } Json::Value toJson( dev::eth::BlockHeader const& _bi, BlockDetails const& _bd, - UncleHashes const& _us, TransactionHashes const& _ts, SealEngineFace* _face ) { + UncleHashes const& _us, TransactionHashes const& _ts, SealEngineFace* _face, u256 _gasPrice ) { Json::Value res = toJson( _bi, _face ); if ( _bi ) { res["totalDifficulty"] = toJS( _bd.totalDifficulty ); res["size"] = toJS( _bd.blockSizeBytes ); res["uncles"] = Json::Value( Json::arrayValue ); + if ( _gasPrice > 0 ) + res["baseFeePerGas"] = toJS( _gasPrice ); for ( h256 h : _us ) res["uncles"].append( toJS( h ) ); res["transactions"] = Json::Value( Json::arrayValue ); @@ -162,7 +186,7 @@ Json::Value toJson( dev::eth::TransactionReceipt const& _t ) { res["gasUsed"] = toJS( _t.cumulativeGasUsed() ); res["bloom"] = toJS( _t.bloom() ); res["log"] = dev::toJson( _t.log() ); - // + std::string strRevertReason = _t.getRevertReason(); if ( !strRevertReason.empty() ) res["revertReason"] = strRevertReason; @@ -181,28 +205,29 @@ Json::Value toJson( dev::eth::LocalisedTransactionReceipt const& _t ) { res["blockNumber"] = toJS( _t.blockNumber() ); res["cumulativeGasUsed"] = toJS( _t.cumulativeGasUsed() ); res["gasUsed"] = toJS( _t.gasUsed() ); - // + // The "contractAddress" field must be null for all types of trasactions but contract deployment // ones. The contract deployment transaction is special because it's the only type of // transaction with "to" filed set to null. - // dev::Address contractAddress = _t.contractAddress(); if ( contractAddress == dev::Address( 0 ) ) res["contractAddress"] = Json::Value::nullRef; else res["contractAddress"] = toJS( contractAddress ); - // - // + res["logs"] = dev::toJson( _t.localisedLogs() ); res["logsBloom"] = toJS( _t.bloom() ); if ( _t.hasStatusCode() ) res["status"] = toString0x< uint8_t >( _t.statusCode() ); // toString( _t.statusCode() ); else res["stateRoot"] = toJS( _t.stateRoot() ); - // + std::string strRevertReason = _t.getRevertReason(); if ( !strRevertReason.empty() ) res["revertReason"] = strRevertReason; + + res["type"] = toJS( _t.txType() ); + res["effectiveGasPrice"] = toJS( _t.effectiveGasPrice() ); return res; } @@ -283,18 +308,16 @@ rapidjson::Document toRapidJson( dev::eth::LocalisedTransactionReceipt const& _t ADD_FIELD_TO_RAPIDJSON( res, "blockNumber", toJS( _t.blockNumber() ), allocator ); ADD_FIELD_TO_RAPIDJSON( res, "cumulativeGasUsed", toJS( _t.cumulativeGasUsed() ), allocator ); ADD_FIELD_TO_RAPIDJSON( res, "gasUsed", toJS( _t.gasUsed() ), allocator ); - // + // The "contractAddress" field must be null for all types of trasactions but contract deployment // ones. The contract deployment transaction is special because it's the only type of // transaction with "to" filed set to null. - // dev::Address contractAddress = _t.contractAddress(); if ( contractAddress == dev::Address( 0 ) ) res.AddMember( "contractAddress", rapidjson::Value(), allocator ); else ADD_FIELD_TO_RAPIDJSON( res, "contractAddress", toJS( contractAddress ), allocator ); - // - // + res.AddMember( "logs", dev::toRapidJson( _t.localisedLogs(), allocator ), allocator ); ADD_FIELD_TO_RAPIDJSON( res, "logsBloom", toJS( _t.bloom() ), allocator ); if ( _t.hasStatusCode() ) { @@ -303,13 +326,15 @@ rapidjson::Document toRapidJson( dev::eth::LocalisedTransactionReceipt const& _t } else { ADD_FIELD_TO_RAPIDJSON( res, "stateRoot", toJS( _t.stateRoot() ), allocator ); } - // std::string strRevertReason = _t.getRevertReason(); if ( !strRevertReason.empty() ) { ADD_FIELD_TO_RAPIDJSON( res, "revertReason", strRevertReason, allocator ); } + ADD_FIELD_TO_RAPIDJSON( res, "type", toJS( _t.txType() ), allocator ); + ADD_FIELD_TO_RAPIDJSON( res, "effectiveGasPrice", toJS( _t.effectiveGasPrice() ), allocator ); + return res; } @@ -319,13 +344,33 @@ Json::Value toJson( dev::eth::Transaction const& _t ) { res["to"] = _t.isCreation() ? Json::Value() : toJS( _t.to() ); res["from"] = toJS( _t.from() ); res["gas"] = toJS( _t.gas() ); - res["gasPrice"] = toJS( _t.gasPrice() ); + if ( _t.txType() != dev::eth::TransactionType::Type2 ) + res["gasPrice"] = toJS( _t.gasPrice() ); res["value"] = toJS( _t.value() ); res["data"] = toJS( _t.data(), 32 ); res["nonce"] = toJS( _t.nonce() ); res["r"] = toJS( _t.signature().r ); res["s"] = toJS( _t.signature().s ); res["v"] = toJS( _t.signature().v ); + res["type"] = toJS( int( _t.txType() ) ); + if ( _t.txType() != dev::eth::TransactionType::Legacy ) { + res["yParity"] = toJS( _t.signature().v ); + res["accessList"] = Json::Value( Json::arrayValue ); + for ( const auto& d : _t.accessList() ) { + auto list = RLP( d ); + Json::Value accessList; + accessList["address"] = dev::toHexPrefixed( list[0].toBytes() ); + accessList["storageKeys"] = Json::Value( Json::arrayValue ); + for ( const auto& k : list[1].toList() ) { + accessList["storageKeys"].append( dev::toHexPrefixed( k.toBytes() ) ); + } + res["accessList"].append( accessList ); + } + if ( _t.txType() != dev::eth::TransactionType::Type1 ) { + res["maxPriorityFeePerGas"] = toJS( _t.maxPriorityFeePerGas() ); + res["maxFeePerGas"] = toJS( _t.maxFeePerGas() ); + } + } } res["hash"] = toJS( _t.sha3( WithSignature ) ); @@ -359,6 +404,25 @@ Json::Value toJson( dev::eth::LocalisedTransaction const& _t ) { toJS( 27 + _t.signature().v ); res["r"] = toJS( _t.signature().r.hex() ); res["s"] = toJS( _t.signature().s.hex() ); + res["type"] = toJS( int( _t.txType() ) ); + if ( _t.txType() != dev::eth::TransactionType::Legacy ) { + res["yParity"] = toJS( _t.signature().v ); + res["accessList"] = Json::Value( Json::arrayValue ); + for ( const auto& d : _t.accessList() ) { + auto list = RLP( d ); + Json::Value accessList; + accessList["address"] = dev::toHexPrefixed( list[0].toBytes() ); + accessList["storageKeys"] = Json::Value( Json::arrayValue ); + for ( const auto& k : list[1].toList() ) { + accessList["storageKeys"].append( dev::toHexPrefixed( k.toBytes() ) ); + } + res["accessList"].append( accessList ); + } + if ( _t.txType() != dev::eth::TransactionType::Type1 ) { + res["maxPriorityFeePerGas"] = toJS( _t.maxPriorityFeePerGas() ); + res["maxFeePerGas"] = toJS( _t.maxPriorityFeePerGas() ); + } + } } return res; } @@ -477,13 +541,18 @@ TransactionSkeleton toTransactionSkeleton( Json::Value const& _json ) { if ( !_json["gasPrice"].empty() ) ret.gasPrice = jsToU256( _json["gasPrice"].asString() ); - - if ( !_json["data"].empty() ) // ethereum.js has preconstructed the data array - ret.data = jsToBytes( _json["data"].asString(), OnFailed::Throw ); + else if ( !_json["maxFeePerGas"].empty() ) + ret.gasPrice = jsToU256( _json["maxFeePerGas"].asString() ); if ( !_json["code"].empty() ) ret.data = jsToBytes( _json["code"].asString(), OnFailed::Throw ); + if ( !_json["data"].empty() ) + ret.data = jsToBytes( _json["data"].asString(), OnFailed::Throw ); + + if ( !_json["input"].empty() ) + ret.data = jsToBytes( _json["input"].asString(), OnFailed::Throw ); + if ( !_json["nonce"].empty() ) ret.nonce = jsToU256( _json["nonce"].asString() ); return ret; @@ -529,19 +598,28 @@ TransactionSkeleton rapidJsonToTransactionSkeleton( rapidjson::Value const& _jso if ( !_json["gasPrice"].IsString() ) throw jsonrpc::JsonRpcException( jsonrpc::Errors::ERROR_RPC_INVALID_PARAMS ); ret.gasPrice = jsToU256( _json["gasPrice"].GetString() ); + } else if ( _json.HasMember( "maxFeePerGas" ) ) { + if ( !_json["maxFeePerGas"].IsString() ) + throw jsonrpc::JsonRpcException( jsonrpc::Errors::ERROR_RPC_INVALID_PARAMS ); + ret.gasPrice = jsToU256( _json["maxFeePerGas"].GetString() ); + } + + if ( _json.HasMember( "code" ) ) { + if ( !_json["code"].IsString() ) + throw jsonrpc::JsonRpcException( jsonrpc::Errors::ERROR_RPC_INVALID_PARAMS ); + ret.data = jsToBytes( _json["code"].GetString(), OnFailed::Throw ); } - if ( _json.HasMember( "data" ) ) { // ethereum.js has preconstructed - // the data array + if ( _json.HasMember( "data" ) ) { if ( !_json["data"].IsString() ) throw jsonrpc::JsonRpcException( jsonrpc::Errors::ERROR_RPC_INVALID_PARAMS ); ret.data = jsToBytes( _json["data"].GetString(), OnFailed::Throw ); } - if ( _json.HasMember( "code" ) ) { - if ( !_json["code"].IsString() ) + if ( _json.HasMember( "input" ) ) { + if ( !_json["input"].IsString() ) throw jsonrpc::JsonRpcException( jsonrpc::Errors::ERROR_RPC_INVALID_PARAMS ); - ret.data = jsToBytes( _json["code"].GetString(), OnFailed::Throw ); + ret.data = jsToBytes( _json["input"].GetString(), OnFailed::Throw ); } if ( _json.HasMember( "nonce" ) ) { @@ -552,37 +630,6 @@ TransactionSkeleton rapidJsonToTransactionSkeleton( rapidjson::Value const& _jso return ret; } -/* -dev::eth::LogFilter toLogFilter( Json::Value const& _json ) { - dev::eth::LogFilter filter; - if ( !_json.isObject() || _json.empty() ) - return filter; - - // check only !empty. it should throw exceptions if input params are incorrect - if ( !_json["fromBlock"].empty() ) - filter.withEarliest( jsToFixed< 32 >( _json["fromBlock"].asString() ) ); - if ( !_json["toBlock"].empty() ) - filter.withLatest( jsToFixed< 32 >( _json["toBlock"].asString() ) ); - if ( !_json["address"].empty() ) { - if ( _json["address"].isArray() ) - for ( auto i : _json["address"] ) - filter.address( jsToAddress( i.asString() ) ); - else - filter.address( jsToAddress( _json["address"].asString() ) ); - } - if ( !_json["topics"].empty() ) - for ( unsigned i = 0; i < _json["topics"].size(); i++ ) { - if ( _json["topics"][i].isArray() ) { - for ( auto t : _json["topics"][i] ) - if ( !t.isNull() ) - filter.topic( i, jsToFixed< 32 >( t.asString() ) ); - } else if ( !_json["topics"][i].isNull() ) // if it is anything else then string, it - // should and will fail - filter.topic( i, jsToFixed< 32 >( _json["topics"][i].asString() ) ); - } - return filter; -} -*/ // TODO: this should be removed once we decide to remove backward compatibility with old log filters dev::eth::LogFilter toLogFilter( Json::Value const& _json ) // commented to avoid warning. diff --git a/libweb3jsonrpc/JsonHelper.h b/libweb3jsonrpc/JsonHelper.h index 9f66e5c00..f5cb095fa 100644 --- a/libweb3jsonrpc/JsonHelper.h +++ b/libweb3jsonrpc/JsonHelper.h @@ -60,9 +60,9 @@ Json::Value toJson( BlockHeader const& _bi, SealEngineFace* _face = nullptr ); Json::Value toJson( Transaction const& _t, std::pair< h256, unsigned > _location, BlockNumber _blockNumber ); Json::Value toJson( BlockHeader const& _bi, BlockDetails const& _bd, UncleHashes const& _us, - Transactions const& _ts, SealEngineFace* _face = nullptr ); + Transactions const& _ts, SealEngineFace* _face = nullptr, u256 _gasPrice = 0 ); Json::Value toJson( BlockHeader const& _bi, BlockDetails const& _bd, UncleHashes const& _us, - TransactionHashes const& _ts, SealEngineFace* _face = nullptr ); + TransactionHashes const& _ts, SealEngineFace* _face = nullptr, u256 _gasPrice = 0 ); Json::Value toJson( TransactionSkeleton const& _t ); Json::Value toJson( Transaction const& _t ); Json::Value toJson( Transaction const& _t, bytes const& _rlp ); diff --git a/skale-key/KeyAux.h b/skale-key/KeyAux.h index 980ba64c9..3ce52128a 100644 --- a/skale-key/KeyAux.h +++ b/skale-key/KeyAux.h @@ -347,10 +347,10 @@ class KeyCLI { t.sign( s ); cout << t.sha3() << ": "; if ( isFile ) { - writeFile( i + ".signed", toHex( t.rlp() ) ); + writeFile( i + ".signed", toHex( t.toBytes() ) ); cout << i + ".signed" << endl; } else - cout << toHex( t.rlp() ) << endl; + cout << toHex( t.toBytes() ) << endl; } catch ( Exception& ex ) { cerr << "Invalid transaction: " << ex.what() << endl; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4a7b264cc..a0a15a45e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -86,8 +86,8 @@ target_link_libraries(testeth PRIVATE ${DEPS_INSTALL_ROOT}/lib/libcrypto.a ${DEPS_INSTALL_ROOT}/lib/libz.a idn2 - ${DEPS_INSTALL_ROOT}/lib/liblzma.a ${DEPS_INSTALL_ROOT}/lib/libunwind.a + ${DEPS_INSTALL_ROOT}/lib/liblzma.a batched-io ) if (CONSENSUS) diff --git a/test/historicstate/hardhat/hardhat.config.js b/test/historicstate/hardhat/hardhat.config.js index 78f7f8c07..c423ba22d 100644 --- a/test/historicstate/hardhat/hardhat.config.js +++ b/test/historicstate/hardhat/hardhat.config.js @@ -3,14 +3,27 @@ require('@openzeppelin/hardhat-upgrades'); /** @type import('hardhat/config').HardhatUserConfig */ module.exports = { - solidity: "0.8.17", + solidity: "0.8.25", }; /* Address 0x907cd0881E50d359bb9Fd120B1A5A143b1C97De6 */ const INSECURE_PRIVATE_KEY = "bd200f4e7f597f3c2c77fb405ee7fabeb249f63f03f43d5927b4fa0c43cfe85e"; module.exports = { - solidity: "0.8.20", + solidity: { + compilers: [ + { + version: `0.8.25`, + settings: { + optimizer: { + enabled: true, + runs: 15000 + }, + evmVersion: `shanghai` + } + }, + ], + }, networks: { skaled: { url: `http://localhost:1234`, diff --git a/test/historicstate/hardhat/scripts/push0_historic_state_patch_test.ts b/test/historicstate/hardhat/scripts/push0_historic_state_patch_test.ts new file mode 100644 index 000000000..3eeede9ba --- /dev/null +++ b/test/historicstate/hardhat/scripts/push0_historic_state_patch_test.ts @@ -0,0 +1,92 @@ +const OWNER_ADDRESS: string = "0x907cd0881E50d359bb9Fd120B1A5A143b1C97De6"; +const ZERO_ADDRESS: string = "0xO000000000000000000000000000000000000000"; +const INITIAL_MINT: bigint = 10000000000000000000000000000000000000000; + +import {ethers} from "hardhat"; + +async function waitUntilNextBlock() { + + const current = await hre.ethers.provider.getBlockNumber(); + let newBlock = current; + console.log(`BLOCK_NUMBER ${current}`); + + while (newBlock == current) { + newBlock = await hre.ethers.provider.getBlockNumber(); + } + + console.log(`BLOCK_NUMBER ${newBlock}`); + + return current; + +} + +function CHECK(result: any): void { + if (!result) { + const message: string = `Check failed ${result}` + console.log(message); + throw message; + } +} + +async function getAndPrintTrace(hash: string): Promise { + + const trace = await ethers.provider.send('debug_traceTransaction', [hash, {}]); + + console.log(JSON.stringify(trace, null, 4)); + return trace; +} + +async function deployWriteAndDestroy(): Promise { + + console.log(`Deploying ...`); + + const Push0Test = await ethers.getContractFactory("Push0"); + const test = await Push0Test.deploy(); + const testContract = await test.deployed(); + + + const deployBn = await ethers.provider.getBlockNumber(); + + const hash = testContract.deployTransaction.hash; + console.log(`Gas limit ${testContract.deployTransaction.gasLimit}`); + console.log(`Contract deployed to ${testContract.address} at block ${deployBn} tx hash ${hash}`); + + console.log(`Now testing`); + + + for (let i = 0; i < 1000; i++) { + + console.log("Current Linux Time in Seconds: ", Math.floor(new Date().getTime() / 1000)); + + const historicBlockNumber = '0x' + parseInt(deployBn).toString(16); + + // Encode the call to the `getZero` method + const data2 = testContract.interface.encodeFunctionData("getZero", []); + + await waitUntilNextBlock(); + + // Create the call transaction object + const callTransaction = { + to: testContract.address, // The address of the contract + data: data2, // The encoded function call + }; + + // Use provider.call to execute the call at the specific historic block number + await ethers.provider.call(callTransaction); + await ethers.provider.call(callTransaction, historicBlockNumber); + + } +} + + + +async function main(): Promise { + await deployWriteAndDestroy(); +} + +// We recommend this pattern to be able to use async/await everywhere +// and properly handle errors. +main().catch((error: any) => { + console.error(error); + process.exitCode = 1; +}); \ No newline at end of file diff --git a/test/historicstate/patches/LegacyVM.cpp.test_timestamp_in_historic_state.patch b/test/historicstate/patches/LegacyVM.cpp.test_timestamp_in_historic_state.patch new file mode 100644 index 000000000..3fd85960d --- /dev/null +++ b/test/historicstate/patches/LegacyVM.cpp.test_timestamp_in_historic_state.patch @@ -0,0 +1,28 @@ +Subject: [PATCH] 1583 Fix crash because of unknown name +--- +Index: libevm/LegacyVM.cpp +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/libevm/LegacyVM.cpp b/libevm/LegacyVM.cpp +--- a/libevm/LegacyVM.cpp (revision a1daeb278f2d38c308366ebfdd39963b939cef90) ++++ b/libevm/LegacyVM.cpp (date 1712249617890) +@@ -1360,9 +1360,14 @@ + // we need to increment program counter only by one since + // the value is not read from program code as in PUSH1 + CASE( PUSH0 ) { +- if ( !m_schedule->havePush0 ) { +- throwBadInstruction(); +- } ++ ++ cerr << ":::::" << m_schedule->havePush0 << endl; ++ cerr << ":::::" << m_schedule->havePush0 << endl; ++ cerr << "Have push0:::::" << m_schedule->havePush0 << endl; ++ cerr << "Have push0:::::" << m_schedule->havePush0 << endl; ++ cerr << ":::::" << m_schedule->havePush0 << endl; ++ cerr << ":::::" << m_schedule->havePush0 << endl; ++ + ON_OP(); + updateIOGas(); + m_SPP[0] = 0; diff --git a/test/jsontests b/test/jsontests index b1cbbff98..98ba9f3fb 160000 --- a/test/jsontests +++ b/test/jsontests @@ -1 +1 @@ -Subproject commit b1cbbff9888a6854f04f58917ab3400395933f5a +Subproject commit 98ba9f3fb0ffdc8bec89b4f623f684eca86e733f diff --git a/test/tools/jsontests/BlockChainTests.cpp b/test/tools/jsontests/BlockChainTests.cpp index ac9dc294f..a99211bf0 100644 --- a/test/tools/jsontests/BlockChainTests.cpp +++ b/test/tools/jsontests/BlockChainTests.cpp @@ -507,10 +507,10 @@ void testBCTest( json_spirit::mObject const& _o ) { if ( blockFromFields.blockHeader().parentHash() == preHash ) { State const postState = testChain.topBlock().state(); assert( testChain.getInterface().sealEngine() ); - bigint reward = calculateMiningReward( testChain.topBlock().blockHeader().number(), + bigint reward = calculateMiningReward( testChain.topBlock().blockHeader().timestamp(), testChain.topBlock().blockHeader().number(), uncleNumbers.size() >= 1 ? uncleNumbers[0] : 0, uncleNumbers.size() >= 2 ? uncleNumbers[1] : 0, - *testChain.getInterface().sealEngine() ); + testChain.getInterface().sealEngine()->chainParams() ); ImportTest::checkBalance( preState, postState, reward ); } else { cnote << "Block Number " << testChain.topBlock().blockHeader().number(); @@ -544,9 +544,9 @@ void testBCTest( json_spirit::mObject const& _o ) { ImportTest::compareStates( postState, blockchain.topBlock().state() ); } -bigint calculateMiningReward( u256 const& _blNumber, u256 const& _unNumber1, u256 const& _unNumber2, - SealEngineFace const& _sealEngine ) { - bigint const baseReward = _sealEngine.blockReward( _blNumber ); +bigint calculateMiningReward( time_t _committedBlockTimestamp, u256 const& _blNumber, u256 const& _unNumber1, u256 const& _unNumber2, + ChainOperationParams const& _cp ) { + bigint const baseReward = _cp.blockReward( _committedBlockTimestamp, _blNumber ); bigint reward = baseReward; // INCLUDE_UNCLE = BASE_REWARD / 32 // UNCLE_REWARD = BASE_REWARD * (8 - Bn + Un) / 8 @@ -958,7 +958,7 @@ void checkBlocks( BOOST_CHECK_MESSAGE( trField == trRlp, _testname + "transactions from rlp and transaction from field do not match" ); BOOST_CHECK_MESSAGE( - trField.rlp() == trRlp.rlp(), _testname + "transactions rlp do not match" ); + trField.toBytes() == trRlp.toBytes(), _testname + "transactions rlp do not match" ); } vector< TestBlock > const& unclesFromField = _blockFromFields.uncles(); diff --git a/test/tools/jsontests/BlockChainTests.h b/test/tools/jsontests/BlockChainTests.h index bfa1f7962..0312859b9 100644 --- a/test/tools/jsontests/BlockChainTests.h +++ b/test/tools/jsontests/BlockChainTests.h @@ -77,8 +77,8 @@ void checkJsonSectionForInvalidBlock( mObject& _blObj ); void checkExpectedException( mObject& _blObj, Exception const& _e ); void checkBlocks( TestBlock const& _blockFromFields, TestBlock const& _blockFromRlp, string const& _testname ); -bigint calculateMiningReward( u256 const& _blNumber, u256 const& _unNumber1, u256 const& _unNumber2, - SealEngineFace const& _sealEngine ); +bigint calculateMiningReward( time_t _committedBlockTimestamp, u256 const& _blNumber, u256 const& _unNumber1, u256 const& _unNumber2, + ChainOperationParams const& _cp ); json_spirit::mObject fillBCTest( json_spirit::mObject const& _input ); void testBCTest( json_spirit::mObject const& _o ); diff --git a/test/tools/jsontests/TransactionTests.cpp b/test/tools/jsontests/TransactionTests.cpp index cd870ca99..96b19dd67 100644 --- a/test/tools/jsontests/TransactionTests.cpp +++ b/test/tools/jsontests/TransactionTests.cpp @@ -102,7 +102,8 @@ json_spirit::mObject FillTransactionTest( json_spirit::mObject const& _o ) { Exception() << errinfo_comment( TestOutputHelper::get().testName() + "transaction from RLP signature is invalid" ) ); - se->verifyTransaction( ImportRequirements::Everything, txFromFields, bh, 0 ); + // TODO Remove SealEngine from tests too! + se->verifyTransaction( se->chainParams(), ImportRequirements::Everything, txFromFields, 0, bh, 0 ); if ( expectSection.count( "sender" ) > 0 ) { string expectSender = toString( expectSection["sender"].get_str() ); BOOST_CHECK_MESSAGE( toString( txFromFields.sender() ) == expectSender, @@ -160,7 +161,8 @@ void TestTransactionTest( json_spirit::mObject const& _o ) { RLP rlp( stream ); txFromRlp = Transaction( rlp.data(), CheckTransaction::Everything ); bool onExperimentalAndZeroSig = onExperimental && txFromRlp.hasZeroSignature(); - se->verifyTransaction( ImportRequirements::Everything, txFromRlp, bh, 0 ); + // TODO Remove SealEngine from tests too! + se->verifyTransaction( se->chainParams(), ImportRequirements::Everything, txFromRlp, 0, bh, 0 ); if ( !( txFromRlp.signature().isValid() || onExperimentalAndZeroSig ) ) BOOST_THROW_EXCEPTION( Exception() << errinfo_comment( testname + diff --git a/test/tools/jsontests/vm.cpp b/test/tools/jsontests/vm.cpp index 4151cc773..939dd07da 100644 --- a/test/tools/jsontests/vm.cpp +++ b/test/tools/jsontests/vm.cpp @@ -92,7 +92,7 @@ mObject FakeExtVM::exportEnv() { return ret; } -EnvInfo FakeExtVM::importEnv( mObject const& _o, LastBlockHashesFace const& _lastBlockHashes ) { +EnvInfo FakeExtVM::importEnv( mObject const& _o, LastBlockHashesFace const& _lastBlockHashes, time_t _committedBlockTimestamp ) { // cant use BOOST_REQUIRE, because this function is used outside boost test (createRandomTest) assert( _o.count( "currentGasLimit" ) > 0 ); assert( _o.count( "currentDifficulty" ) > 0 ); @@ -109,7 +109,7 @@ EnvInfo FakeExtVM::importEnv( mObject const& _o, LastBlockHashesFace const& _las blockHeader.setTimestamp( toPositiveInt64( _o.at( "currentTimestamp" ) ) ); blockHeader.setAuthor( Address( _o.at( "currentCoinbase" ).get_str() ) ); blockHeader.setNumber( toPositiveInt64( _o.at( "currentNumber" ) ) ); - return EnvInfo( blockHeader, _lastBlockHashes, 0, 0 ); + return EnvInfo( blockHeader, _lastBlockHashes, _committedBlockTimestamp, 0, 0 ); } mObject FakeExtVM::exportState() { @@ -313,7 +313,7 @@ json_spirit::mValue VmTestSuite::doTests( json_spirit::mValue const& _input, boo BOOST_REQUIRE_MESSAGE( testInput.count( "expect" ) == 0, testname + " expect set!" ); TestLastBlockHashes lastBlockHashes( h256s( 256, h256() ) ); - eth::EnvInfo env = FakeExtVM::importEnv( testInput.at( "env" ).get_obj(), lastBlockHashes ); + eth::EnvInfo env = FakeExtVM::importEnv( testInput.at( "env" ).get_obj(), lastBlockHashes, 0 ); FakeExtVM fev( env ); fev.importState( testInput.at( "pre" ).get_obj() ); @@ -446,7 +446,8 @@ json_spirit::mValue VmTestSuite::doTests( json_spirit::mValue const& _input, boo BOOST_REQUIRE_MESSAGE( testInput.at( "logs" ).type() == str_type, testname + " logs field is not a string." ); - dev::test::FakeExtVM test( eth::EnvInfo{BlockHeader{}, lastBlockHashes, 0, 0} ); + // use all patches here ("1") + dev::test::FakeExtVM test( eth::EnvInfo{BlockHeader{}, lastBlockHashes, 1, 0, 0} ); test.importState( testInput.at( "post" ).get_obj() ); test.importCallCreates( testInput.at( "callcreates" ).get_array() ); diff --git a/test/tools/jsontests/vm.h b/test/tools/jsontests/vm.h index 80176151a..4c0c510cd 100644 --- a/test/tools/jsontests/vm.h +++ b/test/tools/jsontests/vm.h @@ -77,7 +77,7 @@ class FakeExtVM : public eth::ExtVMFace { u256 doPosts(); json_spirit::mObject exportEnv(); static dev::eth::EnvInfo importEnv( - json_spirit::mObject const& _o, eth::LastBlockHashesFace const& _lastBlockHashes ); + json_spirit::mObject const& _o, eth::LastBlockHashesFace const& _lastBlockHashes, time_t _committedBlockTimestamp ); json_spirit::mObject exportState(); void importState( json_spirit::mObject const& _object ); json_spirit::mObject exportExec(); diff --git a/test/tools/libtesteth/BlockChainHelper.cpp b/test/tools/libtesteth/BlockChainHelper.cpp index 757ac6169..53b3ce334 100644 --- a/test/tools/libtesteth/BlockChainHelper.cpp +++ b/test/tools/libtesteth/BlockChainHelper.cpp @@ -21,7 +21,7 @@ * that manage block/transaction import and test mining */ -#include +#include #include #include @@ -72,7 +72,7 @@ TestBlock::TestBlock( std::string const& _blockRLP ) : TestBlock() { for ( auto const& tr : root[1] ) { Transaction tx( tr.data(), CheckTransaction::Everything ); TestTransaction testTx( tx ); - m_transactionQueue.import( tx.rlp() ); + m_transactionQueue.import( tx.toBytes() ); m_testTransactions.push_back( testTx ); } @@ -121,8 +121,8 @@ void TestBlock::setState( State const& _state ) { void TestBlock::addTransaction( TestTransaction const& _tr ) { m_testTransactions.push_back( _tr ); - if ( m_transactionQueue.import( _tr.transaction().rlp() ) != ImportResult::Success ) - cnote << TestOutputHelper::get().testName() + " Test block failed importing transaction\n"; + if ( m_transactionQueue.import( _tr.transaction().toBytes() ) != ImportResult::Success ) + cnote << TestOutputHelper::get().testName() + " Test block failed importing transaction"; recalcBlockHeaderBytes(); } @@ -383,11 +383,8 @@ void TestBlock::recalcBlockHeaderBytes() { txList.push_back( txi ); RLPStream txStream; txStream.appendList( txList.size() ); - for ( unsigned i = 0; i < txList.size(); ++i ) { - RLPStream txrlp; - txList[i].streamRLP( txrlp ); - txStream.appendRaw( txrlp.out() ); - } + for ( unsigned i = 0; i < txList.size(); ++i ) + txStream.appendRaw( txList[i].toBytes() ); RLPStream uncleStream; uncleStream.appendList( m_uncles.size() ); @@ -444,7 +441,7 @@ void TestBlock::populateFrom( TestBlock const& _original ) { m_transactionQueue.clear(); TransactionQueue const& trQueue = _original.transactionQueue(); for ( auto const& txi : trQueue.topTransactions( std::numeric_limits< unsigned >::max() ) ) - m_transactionQueue.import( txi.rlp() ); + m_transactionQueue.import( txi.toBytes() ); m_uncles = _original.uncles(); m_blockHeader = _original.blockHeader(); @@ -476,8 +473,7 @@ void TestBlockChain::reset( TestBlock const& _genesisBlock ) { bool TestBlockChain::addBlock( TestBlock const& _block ) { - CorrectForkInPowPatch::lastBlockTimestamp = m_blockChain->info().timestamp(); - CorrectForkInPowPatch::lastBlockNumber = m_blockChain->number(); + SchainPatch::useLatestBlockTimestamp(m_blockChain->info().timestamp()); while ( true ) { try { @@ -501,8 +497,7 @@ 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(); + SchainPatch::useLatestBlockTimestamp(m_blockChain->info().timestamp()); return true; } diff --git a/test/tools/libtesteth/ImportTest.cpp b/test/tools/libtesteth/ImportTest.cpp index 7ecf2283d..a805514e8 100644 --- a/test/tools/libtesteth/ImportTest.cpp +++ b/test/tools/libtesteth/ImportTest.cpp @@ -103,7 +103,7 @@ void ImportTest::makeBlockchainTestFromStateTest( set< eth::Network > const& _ne // Calculate the block reward ChainParams const chainParams{genesisInfo( net )}; - EVMSchedule const schedule = chainParams.scheduleForBlockNumber( 1 ); + EVMSchedule const schedule = chainParams.makeEvmSchedule( 0, 1 ); // u256 const blockReward = chainParams.blockReward(schedule); TrExpectSection search{trDup, smap}; @@ -262,11 +262,11 @@ std::tuple< State, ImportTest::ExecOutput, skale::ChangeLog > ImportTest::execut StandardTrace st; st.setShowMnemonics(); st.setOptions( Options::get().jsontraceOptions ); - out = initialState.execute( _env, *se.get(), _tr, Permanence::Committed, st.onOp() ); + out = initialState.execute( _env, se->chainParams(), _tr, Permanence::Committed, st.onOp() ); cout << st.json(); cout << "{\"stateRoot\": \"Is not supported\"}"; } else - out = initialState.execute( _env, *se.get(), _tr, Permanence::Uncommitted ); + out = initialState.execute( _env, se->chainParams(), _tr, Permanence::Uncommitted ); // the changeLog might be broken under --jsontrace, because it uses intialState.execute with // Permanence::Committed rather than Permanence::Uncommitted @@ -357,7 +357,8 @@ void ImportTest::importEnv( json_spirit::mObject const& _o ) { header.setAuthor( Address( _o.at( "currentCoinbase" ).get_str() ) ); m_lastBlockHashes.reset( new TestLastBlockHashes( lastHashes( header.number() ) ) ); - m_envInfo.reset( new EnvInfo( header, *m_lastBlockHashes, 0, mainnetChainID() ) ); + // enable all patches ("1") + m_envInfo.reset( new EnvInfo( header, *m_lastBlockHashes, 1, 0, mainnetChainID() ) ); } // import state from not fully declared json_spirit::mObject, writing to _stateOptionsMap which diff --git a/test/tools/libtesteth/TestHelper.cpp b/test/tools/libtesteth/TestHelper.cpp index 7384cff09..9e8918e51 100644 --- a/test/tools/libtesteth/TestHelper.cpp +++ b/test/tools/libtesteth/TestHelper.cpp @@ -117,7 +117,7 @@ void simulateMining( Client& client, size_t numBlocks, const dev::Address &addre State state = client.state().createStateModifyCopy(); u256 reward = 0; for ( size_t blockNumber = 0; blockNumber < numBlocks; ++blockNumber ) { - reward += client.sealEngine()->blockReward( blockNumber ); + reward += client.sealEngine()->blockReward( 1, blockNumber ); } state.addBalance( address, reward ); state.commit(); diff --git a/test/tools/libtesteth/TestHelper.h b/test/tools/libtesteth/TestHelper.h index 4976ae4c5..aa9a730d1 100644 --- a/test/tools/libtesteth/TestHelper.h +++ b/test/tools/libtesteth/TestHelper.h @@ -84,7 +84,7 @@ typedef json_spirit::Value_type jsonVType; class ZeroGasPricer : public eth::GasPricer { protected: u256 ask( eth::Block const& ) const override { return 0; } - u256 bid( eth::TransactionPriority = eth::TransactionPriority::Medium ) const override { + u256 bid( unsigned = dev::eth::LatestBlock, eth::TransactionPriority = eth::TransactionPriority::Medium ) const override { return 0; } }; diff --git a/test/tools/libtesteth/TestOutputHelper.cpp b/test/tools/libtesteth/TestOutputHelper.cpp index f5ec7cdf2..745896c64 100644 --- a/test/tools/libtesteth/TestOutputHelper.cpp +++ b/test/tools/libtesteth/TestOutputHelper.cpp @@ -20,7 +20,7 @@ * Fixture class for boost output when running testeth */ -#include +#include #include #include #include @@ -103,8 +103,6 @@ void TestOutputHelper::printTestExecStats() { } TestOutputHelperFixture::TestOutputHelperFixture() { TestOutputHelper::get().initTest(); - CorrectForkInPowPatch::lastBlockTimestamp = 1; - CorrectForkInPowPatch::lastBlockNumber = 0; } TestOutputHelperFixture::~TestOutputHelperFixture() { diff --git a/test/tools/libtesteth/boostTest.cpp b/test/tools/libtesteth/boostTest.cpp index 6fd218178..45fb63b2b 100644 --- a/test/tools/libtesteth/boostTest.cpp +++ b/test/tools/libtesteth/boostTest.cpp @@ -137,7 +137,6 @@ void setCLocale() { int main( int argc, const char* argv[] ) { MicroProfileSetEnableAllGroups( true ); UnsafeRegion::init("."); - std::srand( time( nullptr ) ); std::string const dynamicTestSuiteName = "customTestSuite"; setCLocale(); diff --git a/test/unittests/libdevcrypto/crypto.cpp b/test/unittests/libdevcrypto/crypto.cpp index f8a85c56f..0fd4ab8c5 100644 --- a/test/unittests/libdevcrypto/crypto.cpp +++ b/test/unittests/libdevcrypto/crypto.cpp @@ -119,10 +119,10 @@ BOOST_AUTO_TEST_CASE( keypairs ) { p.address() == Address( fromHex( "8a40bfaa73256b60764c1bf40675a99083efb075" ) ) ); eth::Transaction t( 1000, 0, 0, h160( fromHex( "944400f4b88ac9589a0f17ed4671da26bddb668b" ) ), {}, 0, p.secret() ); - auto rlp = t.rlp( eth::WithoutSignature ); + auto rlp = t.toBytes( eth::WithoutSignature ); auto expectedRlp = "dc80808094944400f4b88ac9589a0f17ed4671da26bddb668b8203e880"; BOOST_CHECK_EQUAL( toHex( rlp ), expectedRlp ); - rlp = t.rlp( eth::WithSignature ); + rlp = t.toBytes( eth::WithSignature ); auto expectedRlp2 = "f85f80808094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8801ca0bd2402a510c9c9afddf2a3f63c" "869573bd257475bea91d6f164638134a3386d6a0609ad9775fd2715e6a359c627e9338478e4adba65dd0dc6ef2" diff --git a/test/unittests/libethcore/SealEngineTest.cpp b/test/unittests/libethcore/SealEngineTest.cpp index 003795371..4ee30ce56 100644 --- a/test/unittests/libethcore/SealEngineTest.cpp +++ b/test/unittests/libethcore/SealEngineTest.cpp @@ -61,15 +61,15 @@ BOOST_AUTO_TEST_CASE( UnsignedTransactionIsValidBeforeExperimental, header.setNumber( 1 ); - ethash.SealEngineFace::verifyTransaction( - ImportRequirements::TransactionSignatures, tx, header, 0 ); // check that it doesn't throw + SealEngineFace::verifyTransaction( params, ImportRequirements::TransactionSignatures, + tx, 1, header, 0 ); // check that it doesn't throw } BOOST_AUTO_TEST_CASE( UnsignedTransactionIsValidInExperimental ) { header.setNumber( 0x1010 ); - ethash.SealEngineFace::verifyTransaction( - ImportRequirements::TransactionSignatures, tx, header, 0 ); // check that it doesn't throw + SealEngineFace::verifyTransaction( params, ImportRequirements::TransactionSignatures, + tx, 1, header, 0 ); // check that it doesn't throw } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/unittests/libethereum/ClientTest.cpp b/test/unittests/libethereum/ClientTest.cpp index 81781fa7c..ed9bdb878 100644 --- a/test/unittests/libethereum/ClientTest.cpp +++ b/test/unittests/libethereum/ClientTest.cpp @@ -390,7 +390,8 @@ static std::string const c_genesisInfoSkaleTest = std::string() + "minimumDifficulty": "0x020000", "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", - "blockReward": "0x4563918244F40000" + "blockReward": "0x4563918244F40000", + "skaleDisableChainIdCheck": true }, "genesis": { "nonce": "0x0000000000000042", @@ -410,7 +411,7 @@ static std::string const c_genesisInfoSkaleTest = std::string() + "basePort": )E"+std::to_string( rand_port ) + R"E(, "logLevel": "trace", "logLevelProposal": "trace", - "ecdsaKeyName": "NEK:fa112" + "testSignatures": true }, "sChain": { "schainName": "TestChain", @@ -886,7 +887,7 @@ static std::string const c_genesisInfoSkaleIMABLSPublicKeyTest = std::string() + "basePort": )E"+std::to_string( rand_port ) + R"E(, "logLevel": "trace", "logLevelProposal": "trace", - "ecdsaKeyName": "NEK:fa112" + "testSignatures": true }, "sChain": { "schainName": "TestChain", @@ -1001,7 +1002,7 @@ static std::string const c_skaleConfigString = R"E( "nodeID": 1112, "bindIP": "127.0.0.1", "basePort": )E"+std::to_string( rand_port ) + R"E(, - "ecdsaKeyName": "NEK:fa112" + "testSignatures": true }, "sChain": { "schainName": "TestChain", diff --git a/test/unittests/libethereum/ExtVMTest.cpp b/test/unittests/libethereum/ExtVMTest.cpp index d8e140099..548d30033 100644 --- a/test/unittests/libethereum/ExtVMTest.cpp +++ b/test/unittests/libethereum/ExtVMTest.cpp @@ -49,7 +49,7 @@ class ExtVMConstantinopleFixTestFixture : public TestOutputHelperFixture { } EnvInfo createEnvInfo( BlockHeader const& _header ) const { - return {_header, lastBlockHashes, 0, blockchain.chainID()}; + return {_header, lastBlockHashes, 1, 0, blockchain.chainID()}; } NetworkSelector networkSelector; @@ -72,7 +72,7 @@ BOOST_AUTO_TEST_CASE( BlockhashOutOfBoundsRetunsZero, TestLastBlockHashes lastBlockHashes( {} ); EnvInfo envInfo( createEnvInfo( block.info() ) ); Address addr( "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" ); - ExtVM extVM( block.mutableState(), envInfo, *blockchain.sealEngine(), addr, addr, addr, 0, 0, + ExtVM extVM( block.mutableState(), envInfo, blockchain.sealEngine()->chainParams(), addr, addr, addr, 0, 0, {}, {}, {}, 0, 0, false, false ); BOOST_CHECK_EQUAL( extVM.blockHash( 100 ), h256() ); @@ -85,9 +85,9 @@ BOOST_AUTO_TEST_CASE( BlockhashBeforeConstantinopleReliesOnLastHashes, h256s lastHashes{h256( "0xaaabbbccc" ), h256( "0xdddeeefff" )}; TestLastBlockHashes lastBlockHashes( lastHashes ); - EnvInfo envInfo( block.info(), lastBlockHashes, 0, blockchain.chainID() ); + EnvInfo envInfo( block.info(), lastBlockHashes, block.info().timestamp(), 0, blockchain.chainID() ); Address addr( "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" ); - ExtVM extVM( block.mutableState(), envInfo, *blockchain.sealEngine(), addr, addr, addr, 0, 0, + ExtVM extVM( block.mutableState(), envInfo, blockchain.sealEngine()->chainParams(), addr, addr, addr, 0, 0, {}, {}, {}, 0, 0, false, false ); h256 hash = extVM.blockHash( 1 ); BOOST_REQUIRE_EQUAL( hash, lastHashes[0] ); diff --git a/test/unittests/libethereum/GasPricer.cpp b/test/unittests/libethereum/GasPricer.cpp index 929b64bc5..70bae8380 100644 --- a/test/unittests/libethereum/GasPricer.cpp +++ b/test/unittests/libethereum/GasPricer.cpp @@ -53,8 +53,8 @@ void executeGasPricerTest( string const& name, double _etherPrice, double _block BOOST_CHECK_MESSAGE( abs( gp.ask( Block( Block::Null ) ) - _expectedAsk ) < 100000000, "ASK Got: " + toString( gp.ask( Block( Block::Null ) ) ) + " Expected: " + toString( _expectedAsk ) ); - BOOST_CHECK_MESSAGE( abs( gp.bid( _txPrio ) - _expectedBid ) < 100000000, - "BID Got: " + toString( gp.bid( _txPrio ) ) + " Expected: " + toString( _expectedBid ) ); + BOOST_CHECK_MESSAGE( abs( gp.bid( dev::eth::LatestBlock, _txPrio ) - _expectedBid ) < 100000000, + "BID Got: " + toString( gp.bid( dev::eth::LatestBlock, _txPrio ) ) + " Expected: " + toString( _expectedBid ) ); } } // namespace test } // namespace dev diff --git a/test/unittests/libethereum/PrecompiledConfig.json b/test/unittests/libethereum/PrecompiledConfig.json index b7b4901f0..ec4673de2 100644 --- a/test/unittests/libethereum/PrecompiledConfig.json +++ b/test/unittests/libethereum/PrecompiledConfig.json @@ -38,7 +38,7 @@ "basePort": 1234, "logLevel": "trace", "logLevelProposal": "trace", - "ecdsaKeyName": "NEK:fa112", + "testSignatures": true, "wallets": { "ima": { "n": 1 diff --git a/test/unittests/libethereum/PrecompiledTest.cpp b/test/unittests/libethereum/PrecompiledTest.cpp index 20498f3d9..7be67c511 100644 --- a/test/unittests/libethereum/PrecompiledTest.cpp +++ b/test/unittests/libethereum/PrecompiledTest.cpp @@ -32,8 +32,7 @@ #include #include #include -#include -#include +#include #include @@ -1620,7 +1619,7 @@ static std::string const genesisInfoSkaleConfigTest = std::string() + "basePort": 1234, "logLevel": "trace", "logLevelProposal": "trace", - "ecdsaKeyName": "NEK:fa112", + "testSignatures": true, "wallets": { "ima": { "n": 1 diff --git a/test/unittests/libethereum/SkaleHost.cpp b/test/unittests/libethereum/SkaleHost.cpp index 930339da0..f4b93ce8d 100644 --- a/test/unittests/libethereum/SkaleHost.cpp +++ b/test/unittests/libethereum/SkaleHost.cpp @@ -130,6 +130,7 @@ struct SkaleHostFixture : public TestOutputHelperFixture { chainParams.extraData = h256::random().asBytes(); chainParams.sChain.nodeGroups = { { {}, uint64_t(-1), {"0", "0", "1", "0"} } }; chainParams.nodeInfo.port = chainParams.nodeInfo.port6 = rand_port; + chainParams.nodeInfo.testSignatures = true; chainParams.sChain.nodes[0].port = chainParams.sChain.nodes[0].port6 = rand_port; // not 0-timestamp genesis - to test patch @@ -137,6 +138,8 @@ struct SkaleHostFixture : public TestOutputHelperFixture { if( params.count("multiTransactionMode") && stoi( params.at( "multiTransactionMode" ) ) ) chainParams.sChain.multiTransactionMode = true; + if( params.count("skipInvalidTransactionsPatchTimestamp") && stoi( params.at( "skipInvalidTransactionsPatchTimestamp" ) ) ) + chainParams.sChain._patchTimestamps[static_cast(SchainPatchEnum::SkipInvalidTransactionsPatch)] = stoi( params.at( "skipInvalidTransactionsPatchTimestamp" ) ); accountHolder.reset( new FixedAccountHolder( [&]() { return client.get(); }, {} ) ); accountHolder->setAccounts( {coinbase, account2} ); @@ -177,9 +180,7 @@ struct SkaleHostFixture : public TestOutputHelperFixture { bytes bytes_from_json( const Json::Value& json ) { Transaction tx = tx_from_json( json ); - RLPStream stream; - tx.streamRLP( stream ); - return stream.out(); + return tx.toBytes(); } TransactionQueue* tq; @@ -232,16 +233,17 @@ struct SkaleHostFixture : public TestOutputHelperFixture { { u256 balanceAfter = client->balanceAt( senderAddress ); \ BOOST_REQUIRE_GE( balanceBefore - balanceAfter, decrease ); } -BOOST_FIXTURE_TEST_SUITE( SkaleHostSuite, SkaleHostFixture ) //, *boost::unit_test::disabled() ) +BOOST_AUTO_TEST_SUITE( SkaleHostSuite ) //, *boost::unit_test::disabled() ) auto skipInvalidTransactionsVariants = boost::unit_test::data::make({false, true}); BOOST_DATA_TEST_CASE( validTransaction, skipInvalidTransactionsVariants, skipInvalidTransactionsFlag ) { - if(skipInvalidTransactionsFlag){ - const_cast(client->chainParams()).sChain.skipInvalidTransactionsPatchTimestamp = 1; - } - SkipInvalidTransactionsPatch::setTimestamp(client->chainParams().sChain.skipInvalidTransactionsPatchTimestamp); + SkaleHostFixture fixture( std::map( {{"skipInvalidTransactionsPatchTimestamp", to_string(int(skipInvalidTransactionsFlag))}} ) ); + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& accountHolder = fixture.accountHolder; + auto& stub = fixture.stub; auto senderAddress = coinbase.address(); auto receiver = KeyPair::create(); @@ -259,9 +261,6 @@ BOOST_DATA_TEST_CASE( validTransaction, skipInvalidTransactionsVariants, skipInv pair< bool, Secret > ar = accountHolder->authenticate( ts ); Transaction tx( ts, ar.second ); - RLPStream stream; - tx.streamRLP( stream ); - h256 txHash = tx.sha3(); CHECK_NONCE_BEGIN( senderAddress ); @@ -269,7 +268,7 @@ BOOST_DATA_TEST_CASE( validTransaction, skipInvalidTransactionsVariants, skipInv CHECK_BLOCK_BEGIN; BOOST_REQUIRE_NO_THROW( - stub->createBlock( ConsensusExtFace::transactions_vector{stream.out()}, utcTime(), 1U ) ); + stub->createBlock( ConsensusExtFace::transactions_vector{tx.toBytes()}, utcTime(), 1U ) ); REQUIRE_BLOCK_INCREASE( 1 ); REQUIRE_BLOCK_SIZE( 1, 1 ); @@ -288,10 +287,10 @@ BOOST_DATA_TEST_CASE( transactionRlpBad, skipInvalidTransactionsVariants, skipIn // , *boost::unit_test::precondition( dev::test::run_not_express ) ) { - if(skipInvalidTransactionsFlag){ - const_cast(client->chainParams()).sChain.skipInvalidTransactionsPatchTimestamp = 1; - } - SkipInvalidTransactionsPatch::setTimestamp(client->chainParams().sChain.skipInvalidTransactionsPatchTimestamp); + SkaleHostFixture fixture( std::map( {{"skipInvalidTransactionsPatchTimestamp", to_string(int(skipInvalidTransactionsFlag))}} ) ); + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& stub = fixture.stub; auto senderAddress = coinbase.address(); @@ -374,10 +373,11 @@ BOOST_DATA_TEST_CASE( transactionSigZero, skipInvalidTransactionsVariants, skipI // , *boost::unit_test::precondition( dev::test::run_not_express ) ) { - if(skipInvalidTransactionsFlag){ - const_cast(client->chainParams()).sChain.skipInvalidTransactionsPatchTimestamp = 1; - } - SkipInvalidTransactionsPatch::setTimestamp(client->chainParams().sChain.skipInvalidTransactionsPatchTimestamp); + SkaleHostFixture fixture( std::map( {{"skipInvalidTransactionsPatchTimestamp", to_string(int(skipInvalidTransactionsFlag))}} ) ); + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& accountHolder = fixture.accountHolder; + auto& stub = fixture.stub; auto senderAddress = coinbase.address(); auto receiver = KeyPair::create(); @@ -397,15 +397,12 @@ BOOST_DATA_TEST_CASE( transactionSigZero, skipInvalidTransactionsVariants, skipI VrsHackedTransaction* hacked_tx = reinterpret_cast< VrsHackedTransaction* >( &tx ); hacked_tx->resetSignature(); - RLPStream stream; - tx.streamRLP( stream, WithSignature ); - CHECK_NONCE_BEGIN( senderAddress ); CHECK_BALANCE_BEGIN( senderAddress ); CHECK_BLOCK_BEGIN; BOOST_REQUIRE_NO_THROW( - stub->createBlock( ConsensusExtFace::transactions_vector{stream.out()}, utcTime(), 1U ) ); + stub->createBlock( ConsensusExtFace::transactions_vector{tx.toBytes()}, utcTime(), 1U ) ); REQUIRE_BLOCK_INCREASE( 1 ); @@ -414,7 +411,7 @@ BOOST_DATA_TEST_CASE( transactionSigZero, skipInvalidTransactionsVariants, skipI } else { REQUIRE_BLOCK_SIZE( 1, 1 ); - h256 txHash = sha3( stream.out() ); + h256 txHash = sha3( tx.toBytes() ); REQUIRE_BLOCK_TRANSACTION( 1, 0, txHash ); } @@ -429,10 +426,11 @@ BOOST_DATA_TEST_CASE( transactionSigBad, skipInvalidTransactionsVariants, skipIn // , *boost::unit_test::precondition( dev::test::run_not_express ) ) { - if(skipInvalidTransactionsFlag){ - const_cast(client->chainParams()).sChain.skipInvalidTransactionsPatchTimestamp = 1; - } - SkipInvalidTransactionsPatch::setTimestamp(client->chainParams().sChain.skipInvalidTransactionsPatchTimestamp); + SkaleHostFixture fixture( std::map( {{"skipInvalidTransactionsPatchTimestamp", to_string(int(skipInvalidTransactionsFlag))}} ) ); + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& accountHolder = fixture.accountHolder; + auto& stub = fixture.stub; auto senderAddress = coinbase.address(); auto receiver = KeyPair::create(); @@ -447,9 +445,7 @@ BOOST_DATA_TEST_CASE( transactionSigBad, skipInvalidTransactionsVariants, skipIn pair< bool, Secret > ar = accountHolder->authenticate( ts ); Transaction tx( ts, ar.second ); - RLPStream stream; - tx.streamRLP( stream ); - bytes data = stream.out(); + bytes data = tx.toBytes(); // TODO try to spoil other fields data[43] = 0x7f; // spoil v @@ -484,10 +480,11 @@ BOOST_DATA_TEST_CASE( transactionGasIncorrect, skipInvalidTransactionsVariants, // , *boost::unit_test::precondition( dev::test::run_not_express ) ) { - if(skipInvalidTransactionsFlag){ - const_cast(client->chainParams()).sChain.skipInvalidTransactionsPatchTimestamp = 1; - } - SkipInvalidTransactionsPatch::setTimestamp(client->chainParams().sChain.skipInvalidTransactionsPatchTimestamp); + SkaleHostFixture fixture( std::map( {{"skipInvalidTransactionsPatchTimestamp", to_string(int(skipInvalidTransactionsFlag))}} ) ); + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& accountHolder = fixture.accountHolder; + auto& stub = fixture.stub; auto senderAddress = coinbase.address(); auto receiver = KeyPair::create(); @@ -503,9 +500,6 @@ BOOST_DATA_TEST_CASE( transactionGasIncorrect, skipInvalidTransactionsVariants, pair< bool, Secret > ar = accountHolder->authenticate( ts ); Transaction tx( ts, ar.second ); - RLPStream stream; - tx.streamRLP( stream ); - h256 txHash = tx.sha3(); CHECK_NONCE_BEGIN( senderAddress ); @@ -513,7 +507,7 @@ BOOST_DATA_TEST_CASE( transactionGasIncorrect, skipInvalidTransactionsVariants, CHECK_BLOCK_BEGIN; BOOST_REQUIRE_NO_THROW( - stub->createBlock( ConsensusExtFace::transactions_vector{stream.out()}, utcTime(), 1U ) ); + stub->createBlock( ConsensusExtFace::transactions_vector{tx.toBytes()}, utcTime(), 1U ) ); REQUIRE_BLOCK_INCREASE( 1 ); @@ -537,10 +531,11 @@ BOOST_DATA_TEST_CASE( transactionGasNotEnough, skipInvalidTransactionsVariants, // , *boost::unit_test::precondition( dev::test::run_not_express ) ) { - if(skipInvalidTransactionsFlag){ - const_cast(client->chainParams()).sChain.skipInvalidTransactionsPatchTimestamp = 1; - } - SkipInvalidTransactionsPatch::setTimestamp(client->chainParams().sChain.skipInvalidTransactionsPatchTimestamp); + SkaleHostFixture fixture( std::map( {{"skipInvalidTransactionsPatchTimestamp", to_string(int(skipInvalidTransactionsFlag))}} ) ); + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& accountHolder = fixture.accountHolder; + auto& stub = fixture.stub; auto senderAddress = coinbase.address(); auto receiver = KeyPair::create(); @@ -572,9 +567,6 @@ BOOST_DATA_TEST_CASE( transactionGasNotEnough, skipInvalidTransactionsVariants, pair< bool, Secret > ar = accountHolder->authenticate( ts ); Transaction tx( ts, ar.second ); - RLPStream stream; - tx.streamRLP( stream ); - h256 txHash = tx.sha3(); CHECK_NONCE_BEGIN( senderAddress ); @@ -582,7 +574,7 @@ BOOST_DATA_TEST_CASE( transactionGasNotEnough, skipInvalidTransactionsVariants, CHECK_BLOCK_BEGIN; BOOST_REQUIRE_NO_THROW( - stub->createBlock( ConsensusExtFace::transactions_vector{stream.out()}, utcTime(), 1U ) ); + stub->createBlock( ConsensusExtFace::transactions_vector{tx.toBytes()}, utcTime(), 1U ) ); REQUIRE_BLOCK_INCREASE( 1 ); REQUIRE_BLOCK_SIZE( 1, 1 ); @@ -598,10 +590,11 @@ BOOST_DATA_TEST_CASE( transactionGasNotEnough, skipInvalidTransactionsVariants, // nonce too big BOOST_DATA_TEST_CASE( transactionNonceBig, skipInvalidTransactionsVariants, skipInvalidTransactionsFlag ) { - if(skipInvalidTransactionsFlag){ - const_cast(client->chainParams()).sChain.skipInvalidTransactionsPatchTimestamp = 1; - } - SkipInvalidTransactionsPatch::setTimestamp(client->chainParams().sChain.skipInvalidTransactionsPatchTimestamp); + SkaleHostFixture fixture( std::map( {{"skipInvalidTransactionsPatchTimestamp", to_string(int(skipInvalidTransactionsFlag))}} ) ); + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& accountHolder = fixture.accountHolder; + auto& stub = fixture.stub; auto senderAddress = coinbase.address(); auto receiver = KeyPair::create(); @@ -617,9 +610,6 @@ BOOST_DATA_TEST_CASE( transactionNonceBig, skipInvalidTransactionsVariants, skip pair< bool, Secret > ar = accountHolder->authenticate( ts ); Transaction tx( ts, ar.second ); - RLPStream stream; - tx.streamRLP( stream ); - h256 txHash = tx.sha3(); CHECK_NONCE_BEGIN( senderAddress ); @@ -627,7 +617,7 @@ BOOST_DATA_TEST_CASE( transactionNonceBig, skipInvalidTransactionsVariants, skip CHECK_BLOCK_BEGIN; BOOST_REQUIRE_NO_THROW( - stub->createBlock( ConsensusExtFace::transactions_vector{stream.out()}, utcTime(), 1U ) ); + stub->createBlock( ConsensusExtFace::transactions_vector{tx.toBytes()}, utcTime(), 1U ) ); REQUIRE_BLOCK_INCREASE( 1 ); @@ -650,10 +640,11 @@ BOOST_DATA_TEST_CASE( transactionNonceSmall, skipInvalidTransactionsVariants, sk //, *boost::unit_test::precondition( dev::test::run_not_express ) ) { - if(skipInvalidTransactionsFlag){ - const_cast(client->chainParams()).sChain.skipInvalidTransactionsPatchTimestamp = 1; - } - SkipInvalidTransactionsPatch::setTimestamp(client->chainParams().sChain.skipInvalidTransactionsPatchTimestamp); + SkaleHostFixture fixture( std::map( {{"skipInvalidTransactionsPatchTimestamp", to_string(int(skipInvalidTransactionsFlag))}} ) ); + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& accountHolder = fixture.accountHolder; + auto& stub = fixture.stub; auto senderAddress = coinbase.address(); auto receiver = KeyPair::create(); @@ -669,12 +660,9 @@ BOOST_DATA_TEST_CASE( transactionNonceSmall, skipInvalidTransactionsVariants, sk pair< bool, Secret > ar = accountHolder->authenticate( ts ); Transaction tx1( ts, ar.second ); - RLPStream stream1; - tx1.streamRLP( stream1 ); - // create 1 txns in 1 block BOOST_REQUIRE_NO_THROW( - stub->createBlock( ConsensusExtFace::transactions_vector{stream1.out()}, utcTime(), 1U ) ); + stub->createBlock( ConsensusExtFace::transactions_vector{tx1.toBytes()}, utcTime(), 1U ) ); // now our test txn json["value"] = jsToDecimal( toJS( 9000 * dev::eth::szabo ) ); @@ -683,9 +671,6 @@ BOOST_DATA_TEST_CASE( transactionNonceSmall, skipInvalidTransactionsVariants, sk ar = accountHolder->authenticate( ts ); Transaction tx2( ts, ar.second ); - RLPStream stream2; - tx2.streamRLP( stream2 ); - h256 txHash = tx2.sha3(); CHECK_NONCE_BEGIN( senderAddress ); @@ -693,7 +678,7 @@ BOOST_DATA_TEST_CASE( transactionNonceSmall, skipInvalidTransactionsVariants, sk CHECK_BLOCK_BEGIN; BOOST_REQUIRE_NO_THROW( - stub->createBlock( ConsensusExtFace::transactions_vector{stream2.out()}, utcTime(), 2U ) ); + stub->createBlock( ConsensusExtFace::transactions_vector{tx2.toBytes()}, utcTime(), 2U ) ); REQUIRE_BLOCK_INCREASE( 1 ); @@ -714,10 +699,11 @@ BOOST_DATA_TEST_CASE( transactionNonceSmall, skipInvalidTransactionsVariants, sk // not enough cash BOOST_DATA_TEST_CASE( transactionBalanceBad, skipInvalidTransactionsVariants, skipInvalidTransactionsFlag ) { - if(skipInvalidTransactionsFlag){ - const_cast(client->chainParams()).sChain.skipInvalidTransactionsPatchTimestamp = 1; - } - SkipInvalidTransactionsPatch::setTimestamp(client->chainParams().sChain.skipInvalidTransactionsPatchTimestamp); + SkaleHostFixture fixture( std::map( {{"skipInvalidTransactionsPatchTimestamp", to_string(int(skipInvalidTransactionsFlag))}} ) ); + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& accountHolder = fixture.accountHolder; + auto& stub = fixture.stub; auto senderAddress = coinbase.address(); auto receiver = KeyPair::create(); @@ -733,9 +719,6 @@ BOOST_DATA_TEST_CASE( transactionBalanceBad, skipInvalidTransactionsVariants, sk pair< bool, Secret > ar = accountHolder->authenticate( ts ); Transaction tx( ts, ar.second ); - RLPStream stream; - tx.streamRLP( stream ); - h256 txHash = tx.sha3(); CHECK_NONCE_BEGIN( senderAddress ); @@ -743,7 +726,7 @@ BOOST_DATA_TEST_CASE( transactionBalanceBad, skipInvalidTransactionsVariants, sk CHECK_BLOCK_BEGIN; BOOST_REQUIRE_NO_THROW( - stub->createBlock( ConsensusExtFace::transactions_vector{stream.out()}, utcTime(), 1U ) ); + stub->createBlock( ConsensusExtFace::transactions_vector{tx.toBytes()}, utcTime(), 1U ) ); REQUIRE_BLOCK_INCREASE( 1 ); @@ -771,7 +754,7 @@ BOOST_DATA_TEST_CASE( transactionBalanceBad, skipInvalidTransactionsVariants, sk // make money dev::eth::simulateMining( *client, 1, senderAddress ); - stub->createBlock( ConsensusExtFace::transactions_vector{stream.out()}, utcTime(), 2U ); + stub->createBlock( ConsensusExtFace::transactions_vector{tx.toBytes()}, utcTime(), 2U ); REQUIRE_BLOCK_SIZE( 2, 1 ); REQUIRE_BLOCK_TRANSACTION( 2, 0, txHash ); @@ -792,10 +775,10 @@ BOOST_DATA_TEST_CASE( transactionGasBlockLimitExceeded, skipInvalidTransactionsV // , *boost::unit_test::precondition( dev::test::run_not_express ) ) { - if(skipInvalidTransactionsFlag){ - const_cast(client->chainParams()).sChain.skipInvalidTransactionsPatchTimestamp = 1; - } - SkipInvalidTransactionsPatch::setTimestamp(client->chainParams().sChain.skipInvalidTransactionsPatchTimestamp); + SkaleHostFixture fixture( std::map( {{"skipInvalidTransactionsPatchTimestamp", to_string(int(skipInvalidTransactionsFlag))}} ) ); + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& stub = fixture.stub; auto senderAddress = coinbase.address(); auto receiver = KeyPair::create(); @@ -808,10 +791,7 @@ BOOST_DATA_TEST_CASE( transactionGasBlockLimitExceeded, skipInvalidTransactionsV json["nonce"] = 0; json["gasPrice"] = 0; - Transaction tx1 = tx_from_json( json ); - - RLPStream stream1; - tx1.streamRLP( stream1 ); + Transaction tx1 = fixture.tx_from_json( json ); h256 txHash1 = tx1.sha3(); @@ -820,10 +800,7 @@ BOOST_DATA_TEST_CASE( transactionGasBlockLimitExceeded, skipInvalidTransactionsV json["nonce"] = 1; json["gas"] = jsToDecimal( toJS( client->chainParams().gasLimit - 21000 + 1 ) ); - Transaction tx2 = tx_from_json( json ); - - RLPStream stream2; - tx2.streamRLP( stream2 ); + Transaction tx2 = fixture.tx_from_json( json ); h256 txHash2 = tx2.sha3(); @@ -832,7 +809,7 @@ BOOST_DATA_TEST_CASE( transactionGasBlockLimitExceeded, skipInvalidTransactionsV CHECK_BLOCK_BEGIN; BOOST_REQUIRE_NO_THROW( stub->createBlock( - ConsensusExtFace::transactions_vector{stream1.out(), stream2.out()}, utcTime(), 1U ) ); + ConsensusExtFace::transactions_vector{tx1.toBytes(), tx2.toBytes()}, utcTime(), 1U ) ); BOOST_REQUIRE_EQUAL( client->number(), 1 ); REQUIRE_BLOCK_INCREASE( 1 ); @@ -855,11 +832,19 @@ BOOST_DATA_TEST_CASE( transactionGasBlockLimitExceeded, skipInvalidTransactionsV // Last transaction should be dropped from block proposal BOOST_AUTO_TEST_CASE( gasLimitInBlockProposal ) { + + SkaleHostFixture fixture; + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& skaleHost = fixture.skaleHost; + auto& stub = fixture.stub; + auto& account2 = fixture.account2; + auto receiver = KeyPair::create(); { auto wr_state = client->state().createStateModifyCopy(); - wr_state.addBalance( account2.address(), client->chainParams().gasLimit * 1000 + dev::eth::ether ); + wr_state.addBalance( fixture.account2.address(), client->chainParams().gasLimit * 1000 + dev::eth::ether ); wr_state.commit(); } @@ -871,36 +856,38 @@ BOOST_AUTO_TEST_CASE( gasLimitInBlockProposal ) { json["nonce"] = 0; json["gasPrice"] = 1000; - Transaction tx1 = tx_from_json( json ); - - RLPStream stream1; - tx1.streamRLP( stream1 ); + Transaction tx1 = fixture.tx_from_json( json ); // 2 txn json["from"] = toJS( account2.address() ); json["gas"] = jsToDecimal( toJS( client->chainParams().gasLimit - 21000 + 1 ) ); - Transaction tx2 = tx_from_json( json ); - - RLPStream stream2; - tx2.streamRLP( stream2 ); + Transaction tx2 = fixture.tx_from_json( json ); // put already broadcasted txns - skaleHost->receiveTransaction( toJS( stream1.out() ) ); - skaleHost->receiveTransaction( toJS( stream2.out() ) ); + skaleHost->receiveTransaction( toJS( tx1.toBytes() ) ); + skaleHost->receiveTransaction( toJS( tx2.toBytes() ) ); sleep( 1 ); // allow broadcast thread to move them ConsensusExtFace::transactions_vector proposal = stub->pendingTransactions( 100 ); BOOST_REQUIRE_EQUAL( proposal.size(), 1 ); - BOOST_REQUIRE( proposal[0] == stream1.out() ); + BOOST_REQUIRE( proposal[0] == tx1.toBytes() ); } // positive test for 4 next ones BOOST_AUTO_TEST_CASE( transactionDropReceive //, *boost::unit_test::precondition( dev::test::run_not_express ) ) { + + SkaleHostFixture fixture; + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& skaleHost = fixture.skaleHost; + auto& stub = fixture.stub; + auto& tq = fixture.tq; + auto senderAddress = coinbase.address(); auto receiver = KeyPair::create(); @@ -912,8 +899,8 @@ BOOST_AUTO_TEST_CASE( transactionDropReceive json["nonce"] = 1; // 1st tx - Transaction tx1 = tx_from_json( json ); - tx1.checkOutExternalGas( client->chainParams(), client->number() ); + Transaction tx1 = fixture.tx_from_json( json ); + tx1.checkOutExternalGas( client->chainParams(), client->latestBlock().info().timestamp(), client->number(), false ); // submit it! tq->import( tx1 ); @@ -922,7 +909,7 @@ BOOST_AUTO_TEST_CASE( transactionDropReceive u256 value2 = 20000 * dev::eth::szabo; json["value"] = jsToDecimal( toJS( value2 ) ); json["nonce"] = 0; - bytes tx2 = bytes_from_json( json ); + bytes tx2 = fixture.bytes_from_json( json ); // receive it! skaleHost->receiveTransaction( toJS( tx2 ) ); @@ -935,7 +922,7 @@ BOOST_AUTO_TEST_CASE( transactionDropReceive json["value"] = jsToDecimal( toJS( value3 ) ); json["nonce"] = 0; - bytes tx3 = bytes_from_json( json ); + bytes tx3 = fixture.bytes_from_json( json ); // return it from consensus! CHECK_BLOCK_BEGIN; @@ -957,6 +944,13 @@ BOOST_AUTO_TEST_CASE( transactionDropReceive BOOST_AUTO_TEST_CASE( transactionDropQueue, *boost::unit_test::precondition( dev::test::run_not_express ) ) { + + SkaleHostFixture fixture; + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& stub = fixture.stub; + auto& tq = fixture.tq; + auto senderAddress = coinbase.address(); auto receiver = KeyPair::create(); @@ -969,8 +963,8 @@ BOOST_AUTO_TEST_CASE( transactionDropQueue, json["nonce"] = 1; // 1st tx - Transaction tx1 = tx_from_json( json ); - tx1.checkOutExternalGas( client->chainParams(), client->number() ); + Transaction tx1 = fixture.tx_from_json( json ); + tx1.checkOutExternalGas( client->chainParams(), client->latestBlock().info().timestamp(), client->number(), false ); // submit it! tq->import( tx1 ); @@ -983,10 +977,7 @@ BOOST_AUTO_TEST_CASE( transactionDropQueue, json["value"] = jsToDecimal( toJS( value2 ) ); json["nonce"] = 0; - Transaction tx2 = tx_from_json( json ); - - RLPStream stream2; - tx2.streamRLP( stream2 ); + Transaction tx2 = fixture.tx_from_json( json ); h256 txHash2 = tx2.sha3(); @@ -996,7 +987,7 @@ BOOST_AUTO_TEST_CASE( transactionDropQueue, CHECK_BLOCK_BEGIN; BOOST_REQUIRE_NO_THROW( - stub->createBlock( ConsensusExtFace::transactions_vector{stream2.out()}, utcTime(), 1U ) ); + stub->createBlock( ConsensusExtFace::transactions_vector{tx2.toBytes()}, utcTime(), 1U ) ); stub->setPriceForBlockId( 1, 1000 ); REQUIRE_BLOCK_INCREASE( 1 ); @@ -1014,6 +1005,13 @@ BOOST_AUTO_TEST_CASE( transactionDropQueue, BOOST_AUTO_TEST_CASE( transactionDropByGasPrice // , *boost::unit_test::precondition( dev::test::run_not_express ) ) { + + SkaleHostFixture fixture; + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& stub = fixture.stub; + auto& tq = fixture.tq; + auto senderAddress = coinbase.address(); auto receiver = KeyPair::create(); @@ -1026,8 +1024,8 @@ BOOST_AUTO_TEST_CASE( transactionDropByGasPrice json["nonce"] = 1; // 1st tx - Transaction tx1 = tx_from_json( json ); - tx1.checkOutExternalGas( client->chainParams(), client->number() ); + Transaction tx1 = fixture.tx_from_json( json ); + tx1.checkOutExternalGas( client->chainParams(), client->latestBlock().info().timestamp(), client->number(), false ); // submit it! tq->import( tx1 ); @@ -1040,10 +1038,7 @@ BOOST_AUTO_TEST_CASE( transactionDropByGasPrice json["value"] = jsToDecimal( toJS( value2 ) ); json["nonce"] = 0; - Transaction tx2 = tx_from_json( json ); - - RLPStream stream2; - tx2.streamRLP( stream2 ); + Transaction tx2 = fixture.tx_from_json( json ); h256 txHash2 = tx2.sha3(); @@ -1053,7 +1048,7 @@ BOOST_AUTO_TEST_CASE( transactionDropByGasPrice CHECK_BLOCK_BEGIN; BOOST_REQUIRE_NO_THROW( stub->createBlock( - ConsensusExtFace::transactions_vector{stream2.out()}, utcTime(), 1U, 1000 ) ); + ConsensusExtFace::transactions_vector{tx2.toBytes()}, utcTime(), 1U, 1000 ) ); stub->setPriceForBlockId( 1, 1100 ); REQUIRE_BLOCK_INCREASE( 1 ); @@ -1071,12 +1066,21 @@ BOOST_AUTO_TEST_CASE( transactionDropByGasPrice BOOST_AUTO_TEST_CASE( transactionDropByGasPriceReceive // , *boost::unit_test::precondition( dev::test::run_not_express ) ) { + + SkaleHostFixture fixture; + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& skaleHost = fixture.skaleHost; + auto& stub = fixture.stub; + auto& tq = fixture.tq; + auto& account2 = fixture.account2; + auto senderAddress = coinbase.address(); auto receiver = KeyPair::create(); { auto wr_state = client->state().createStateModifyCopy(); - wr_state.addBalance( account2.address(), 1 * ether ); + wr_state.addBalance( fixture.account2.address(), 1 * ether ); wr_state.commit(); } @@ -1089,14 +1093,11 @@ BOOST_AUTO_TEST_CASE( transactionDropByGasPriceReceive json["gasPrice"] = "1000"; // 1st tx - Transaction tx1 = tx_from_json( json ); - tx1.checkOutExternalGas( client->chainParams(), client->number() ); - - RLPStream stream1; - tx1.streamRLP( stream1 ); + Transaction tx1 = fixture.tx_from_json( json ); + tx1.checkOutExternalGas( client->chainParams(), client->latestBlock().info().timestamp(), client->number(), false ); // receive it! - skaleHost->receiveTransaction( toJS( stream1.out() ) ); + skaleHost->receiveTransaction( toJS( tx1.toBytes() ) ); sleep( 1 ); BOOST_REQUIRE_EQUAL( tq->knownTransactions().size(), 1 ); @@ -1107,10 +1108,7 @@ BOOST_AUTO_TEST_CASE( transactionDropByGasPriceReceive json["value"] = jsToDecimal( toJS( value2 ) ); json["nonce"] = 0; - Transaction tx2 = tx_from_json( json ); - - RLPStream stream2; - tx2.streamRLP( stream2 ); + Transaction tx2 = fixture.tx_from_json( json ); h256 txHash2 = tx2.sha3(); @@ -1120,7 +1118,7 @@ BOOST_AUTO_TEST_CASE( transactionDropByGasPriceReceive CHECK_BLOCK_BEGIN; BOOST_REQUIRE_NO_THROW( stub->createBlock( - ConsensusExtFace::transactions_vector{stream2.out()}, utcTime(), 1U, 1000 ) ); + ConsensusExtFace::transactions_vector{tx2.toBytes()}, utcTime(), 1U, 1000 ) ); stub->setPriceForBlockId( 1, 1100 ); REQUIRE_BLOCK_INCREASE( 1 ); @@ -1137,6 +1135,12 @@ BOOST_AUTO_TEST_CASE( transactionDropByGasPriceReceive BOOST_AUTO_TEST_CASE( transactionRace // , *boost::unit_test::precondition( dev::test::run_not_express ) ) { + + SkaleHostFixture fixture; + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& stub = fixture.stub; + auto senderAddress = coinbase.address(); auto receiver = KeyPair::create(); @@ -1149,10 +1153,7 @@ BOOST_AUTO_TEST_CASE( transactionRace json["gasPrice"] = jsToDecimal( toJS( gasPrice ) ); json["nonce"] = 0; - Transaction tx = tx_from_json( json ); - - RLPStream stream; - tx.streamRLP( stream ); + Transaction tx = fixture.tx_from_json( json ); h256 txHash = tx.sha3(); @@ -1165,7 +1166,7 @@ BOOST_AUTO_TEST_CASE( transactionRace // 2 get it from consensus BOOST_REQUIRE_NO_THROW( - stub->createBlock( ConsensusExtFace::transactions_vector{stream.out()}, utcTime(), 1U ) ); + stub->createBlock( ConsensusExtFace::transactions_vector{tx.toBytes()}, utcTime(), 1U ) ); stub->setPriceForBlockId( 1, 1000 ); REQUIRE_BLOCK_INCREASE( 1 ); @@ -1181,7 +1182,7 @@ BOOST_AUTO_TEST_CASE( transactionRace // 3 send new tx and see nonce json["nonce"] = 1; - Transaction tx2 = tx_from_json( json ); + Transaction tx2 = fixture.tx_from_json( json ); client->importTransaction( tx2 ); } @@ -1190,6 +1191,13 @@ BOOST_AUTO_TEST_CASE( transactionRace BOOST_AUTO_TEST_CASE( partialCatchUp // , *boost::unit_test::precondition( dev::test::run_not_express ) ) { + + SkaleHostFixture fixture; + auto& client = fixture.client; + auto& coinbase = fixture.coinbase; + auto& accountHolder = fixture.accountHolder; + auto& stub = fixture.stub; + auto senderAddress = coinbase.address(); auto receiver = KeyPair::create(); @@ -1204,12 +1212,9 @@ BOOST_AUTO_TEST_CASE( partialCatchUp pair< bool, Secret > ar = accountHolder->authenticate( ts ); Transaction tx1( ts, ar.second ); - RLPStream stream1; - tx1.streamRLP( stream1 ); - // create 1 txns in 1 block BOOST_REQUIRE_NO_THROW( - stub->createBlock( ConsensusExtFace::transactions_vector{stream1.out()}, utcTime(), 1U ) ); + stub->createBlock( ConsensusExtFace::transactions_vector{tx1.toBytes()}, utcTime(), 1U ) ); // now 2 txns json["value"] = jsToDecimal( toJS( 9000 * dev::eth::szabo ) ); @@ -1218,9 +1223,6 @@ BOOST_AUTO_TEST_CASE( partialCatchUp ar = accountHolder->authenticate( ts ); Transaction tx2( ts, ar.second ); - RLPStream stream2; - tx2.streamRLP( stream2 ); - h256 txHash = tx2.sha3(); CHECK_NONCE_BEGIN( senderAddress ); @@ -1228,7 +1230,7 @@ BOOST_AUTO_TEST_CASE( partialCatchUp CHECK_BLOCK_BEGIN; BOOST_REQUIRE_NO_THROW( - stub->createBlock( ConsensusExtFace::transactions_vector{stream1.out(), stream2.out()}, utcTime(), 2U ) ); + stub->createBlock( ConsensusExtFace::transactions_vector{tx1.toBytes(), tx2.toBytes()}, utcTime(), 2U ) ); REQUIRE_BLOCK_INCREASE( 1 ); REQUIRE_BLOCK_SIZE( 2, 2 ); @@ -1239,6 +1241,10 @@ BOOST_AUTO_TEST_CASE( partialCatchUp } BOOST_AUTO_TEST_CASE( getBlockRandom ) { + + SkaleHostFixture fixture; + auto& skaleHost = fixture.skaleHost; + PrecompiledExecutor exec = PrecompiledRegistrar::executor( "getBlockRandom" ); auto res = exec( bytesConstRef() ); u256 blockRandom = skaleHost->getBlockRandom(); @@ -1247,6 +1253,10 @@ BOOST_AUTO_TEST_CASE( getBlockRandom ) { } BOOST_AUTO_TEST_CASE( getIMABLSPUblicKey ) { + + SkaleHostFixture fixture; + auto& skaleHost = fixture.skaleHost; + PrecompiledExecutor exec = PrecompiledRegistrar::executor( "getIMABLSPublicKey" ); auto res = exec( bytesConstRef() ); std::array< std::string, 4 > imaBLSPublicKey = skaleHost->getIMABLSPublicKey(); @@ -1284,13 +1294,10 @@ BOOST_FIXTURE_TEST_CASE( mtmAfterBigNonceMined, dummy, pair< bool, Secret > ar = accountHolder->authenticate( ts ); Transaction tx1( ts, ar.second ); - RLPStream stream1; - tx1.streamRLP( stream1 ); - h256 tx1Hash = tx1.sha3(); // it will be put to "future" queue - skaleHost->receiveTransaction( toJS( stream1.out() ) ); + skaleHost->receiveTransaction( toJS( tx1.toBytes() ) ); sleep( 1 ); ConsensusExtFace::transactions_vector proposal = stub->pendingTransactions( 100 ); // and not proposed @@ -1301,7 +1308,7 @@ BOOST_FIXTURE_TEST_CASE( mtmAfterBigNonceMined, dummy, // simulate it coming from another node BOOST_REQUIRE_NO_THROW( - stub->createBlock( ConsensusExtFace::transactions_vector{stream1.out()}, utcTime(), 1U ) ); + stub->createBlock( ConsensusExtFace::transactions_vector{tx1.toBytes()}, utcTime(), 1U ) ); REQUIRE_BLOCK_SIZE( 1, 1 ); REQUIRE_BLOCK_TRANSACTION( 1, 0, tx1Hash ); @@ -1314,13 +1321,10 @@ BOOST_FIXTURE_TEST_CASE( mtmAfterBigNonceMined, dummy, ar = accountHolder->authenticate( ts ); Transaction tx2( ts, ar.second ); - RLPStream stream2; - tx2.streamRLP( stream2 ); - h256 tx2Hash = tx2.sha3(); // post it to queue for "realism" - skaleHost->receiveTransaction( toJS( stream2.out() ) ); + skaleHost->receiveTransaction( toJS( tx2.toBytes() ) ); sleep( 1 ); proposal = stub->pendingTransactions( 100 ); BOOST_REQUIRE_EQUAL(proposal.size(), 2); @@ -1337,7 +1341,7 @@ BOOST_FIXTURE_TEST_CASE( mtmAfterBigNonceMined, dummy, // 3 submit nonce = 1 again! // it should go to proposal BOOST_REQUIRE_THROW( - skaleHost->receiveTransaction( toJS( stream1.out() ) ), + skaleHost->receiveTransaction( toJS( tx1.toBytes() ) ), dev::eth::PendingTransactionAlreadyExists ); sleep( 1 ); diff --git a/test/unittests/libethereum/Transaction.cpp b/test/unittests/libethereum/Transaction.cpp index 14e53d159..3104cc46f 100644 --- a/test/unittests/libethereum/Transaction.cpp +++ b/test/unittests/libethereum/Transaction.cpp @@ -41,7 +41,29 @@ BOOST_AUTO_TEST_CASE( TransactionGasRequired, "79f984b031ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0ef" "ffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804" ), CheckTransaction::None ); - BOOST_CHECK_EQUAL( tr.baseGasRequired( FrontierSchedule ), 14 * 68 + 21000 ); + BOOST_CHECK_EQUAL( tr.baseGasRequired( HomesteadSchedule ), 14 * 68 + 21000 ); + BOOST_CHECK_EQUAL( tr.baseGasRequired( IstanbulSchedule ), 14 * 16 + 21000 ); + + tr = Transaction ( + fromHex( "0x01f8d18197808504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b018" + "e0358ac39584bc98a7c979f984b03f85bf85994de0b295669a9fd93d5f28d9ec85e40f4cb697b" + "aef842a00000000000000000000000000000000000000000000000000000000000000003a0000" + "000000000000000000000000000000000000000000000000000000000000780a08ae3a721ee02" + "cf52d85ecec934c6f46ea3e96d6355eb8ccde261e1e419885761a0234565f6d227d8eba0937b0" + "f03cb25f83aeb24c13b7a39a9ef6e80c1ea272a3c" ), + CheckTransaction::None, false, true ); + BOOST_CHECK_EQUAL( tr.baseGasRequired( HomesteadSchedule ), 14 * 68 + 21000 ); + BOOST_CHECK_EQUAL( tr.baseGasRequired( IstanbulSchedule ), 14 * 16 + 21000 ); + + tr = Transaction ( + fromHex( "0x02f8d78197808504a817c8008504a817c800827530947d36af85a184e220a656525fcbb9a63" + "b9ab3c12b018e0358ac39584bc98a7c979f984b03f85bf85994de0b295669a9fd93d5f28d9ec8" + "5e40f4cb697baef842a0000000000000000000000000000000000000000000000000000000000" + "0000003a0000000000000000000000000000000000000000000000000000000000000000780a0" + "23927f0e208494bd1fd8876597899d72025167fed902e9c1c417ddd8639bb7b4a02a63ea48f7e" + "94df3a40c4a840ba98da02f13817acb5fe137d40f632e6c8ed367" ), + CheckTransaction::None, false, true ); + BOOST_CHECK_EQUAL( tr.baseGasRequired( HomesteadSchedule ), 14 * 68 + 21000 ); BOOST_CHECK_EQUAL( tr.baseGasRequired( IstanbulSchedule ), 14 * 16 + 21000 ); } @@ -57,6 +79,40 @@ BOOST_AUTO_TEST_CASE( TransactionWithEmptyRecepient ) { "0xf84c8014830493e0c0808026a02f23977c68f851bbec8619510a4acdd34805270d97f5714b003efe7274914c" "a2a05874022b26e0d88807bdcc59438f86f5a82e24afefad5b6a67ae853896fe2b37" ); BOOST_REQUIRE_THROW( Transaction( txRlp, CheckTransaction::None ), InvalidTransactionFormat ); + + txRlp = fromHex( + "0x01f8bd8197808504a817c80082753080018e0358ac39584bc98a7c979f984b03f85bf85994de0b295669a9fd" + "93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000" + "000003a0000000000000000000000000000000000000000000000000000000000000000780a08d795591e0eb53" + "fb374a804ba3f73cf291069549d62316219811c3f7fb8cfad0a07e9d0bd7fabc8f74475624c912b5334dc49224" + "b1dede6c802d52a35254bfc457" ); + tx = Transaction( txRlp, CheckTransaction::None, false, true ); // shouldn't throw + + // recipient RLP is 0xc0 (empty list) + txRlp = fromHex( + "0x01f8bd8197808504a817c800827530c0018e0358ac39584bc98a7c979f984b03f85bf85994de0b295669a9fd" + "93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000" + "000003a0000000000000000000000000000000000000000000000000000000000000000780a08d795591e0eb53" + "fb374a804ba3f73cf291069549d62316219811c3f7fb8cfad0a07e9d0bd7fabc8f74475624c912b5334dc49224" + "b1dede6c802d52a35254bfc457" ); + BOOST_REQUIRE_THROW( Transaction( txRlp, CheckTransaction::None, false, true ), InvalidTransactionFormat ); + + txRlp = fromHex( + "0x02f8c38197808504a817c8008504a817c80082753080018e0358ac39584bc98a7c979f984b03f85bf85994de" + "0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000" + "000000000000000003a0000000000000000000000000000000000000000000000000000000000000000780a0c8" + "029a8b702d54c79ef18b557e755a1bfd8a4afcfcf31813790df34a6f740a95a00ceb8fdf611b4c9ff8d007d2a5" + "44bc4bfae0e97a03e32b1c8b8208c82cebcafb" ); + tx = Transaction( txRlp, CheckTransaction::None, false, true ); // shouldn't throw + + // recipient RLP is 0xc0 (empty list) + txRlp = fromHex( + "0x02f8c38197808504a817c8008504a817c800827530c0018e0358ac39584bc98a7c979f984b03f85bf85994de" + "0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000" + "000000000000000003a0000000000000000000000000000000000000000000000000000000000000000780a0c8" + "029a8b702d54c79ef18b557e755a1bfd8a4afcfcf31813790df34a6f740a95a00ceb8fdf611b4c9ff8d007d2a5" + "44bc4bfae0e97a03e32b1c8b8208c82cebcafb" ); + BOOST_REQUIRE_THROW( Transaction( txRlp, CheckTransaction::None, false, true ), InvalidTransactionFormat ); } BOOST_AUTO_TEST_CASE( TransactionNotReplayProtected, @@ -66,11 +122,26 @@ BOOST_AUTO_TEST_CASE( TransactionNotReplayProtected, "1ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3" "b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804" ); Transaction tx( txRlp, CheckTransaction::None ); - tx.checkChainId( 1234, false ); // any chain ID is accepted for not replay protected tx + tx.checkChainId( 1234, true ); // any chain ID is accepted for not replay protected tx + + BOOST_REQUIRE( tx.toBytes() == txRlp ); - RLPStream txRlpStream; - tx.streamRLP( txRlpStream ); - BOOST_REQUIRE( txRlpStream.out() == txRlp ); + txRlp = fromHex( + "0x01f8ce8504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b018e0358ac3958" + "4bc98a7c979f984b03f85bf85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a0000000000000" + "0000000000000000000000000000000000000000000000000003a000000000000000000000000000000000" + "0000000000000000000000000000000701a0a3b1de6f2958e1e34db86438bba310637f2e799fe9768a143a" + "d87e47c33d1e6ca00e04ef9fe6bb01176c5a4c5bf4a070662478a320eaaff2895d17451c8d61d472" ); + BOOST_REQUIRE_THROW( Transaction( txRlp, CheckTransaction::None, false, true ), dev::BadCast ); + + txRlp = fromHex( + "0x02f8d5808504a817c8008504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b" + "018e0358ac39584bc98a7c979f984b03f85bf85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842" + "a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000" + "000000000000000000000000000000000000000000000780a023927f0e208494bd1fd8876597899d720251" + "67fed902e9c1c417ddd8639bb7b4a02a63ea48f7e94df3a40c4a840ba98da02f13817acb5fe137d40f632e" + "6c8ed367" ); + BOOST_REQUIRE_THROW( Transaction( txRlp, CheckTransaction::None, false, true ), dev::BadCast ); } BOOST_AUTO_TEST_CASE( TransactionChainIDMax64Bit, @@ -90,6 +161,25 @@ BOOST_AUTO_TEST_CASE( TransactionChainIDMax64Bit, "789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3" ); Transaction tx2{txRlp2, CheckTransaction::None}; tx2.checkChainId( std::numeric_limits< uint64_t >::max(), false ); + + txRlp1 = fromHex( + "0x01f8d888ffffffffffffffff808504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b01" + "8e0358ac39584bc98a7c979f984b03f85bf85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000" + "000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000" + "00000000000000000000000000000000000701a0e236de02b843139aebfce593d680c06ce79cfd2f2e7f9dcac9" + "fe23b38060591aa0734952245446ad42e47ec996c9a7b02973cbc8dd944c9622714416b2bef122f4" ); + tx1 = Transaction{txRlp1, CheckTransaction::None, false, true}; + tx1.checkChainId( std::numeric_limits< uint64_t >::max(), false ); + + txRlp1 = fromHex( + "0x02f8de88ffffffffffffffff808504a817c8008504a817c800827530947d36af85a184e220a656525fcbb9a6" + "3b9ab3c12b018e0358ac39584bc98a7c979f984b03f85bf85994de0b295669a9fd93d5f28d9ec85e40f4cb697b" + "aef842a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000" + "00000000000000000000000000000000000000000000000780a0b62465e633b565f2f3632125b452d8df66d4f6" + "b48b58f59da6201234e3f9ce75a0467f18ca2b64f3642cb37e7d5470bbac5fbc62c66b23a0ff955b994803fcf3" + "74" ); + tx1 = Transaction{txRlp1, CheckTransaction::None, false, true}; + tx1.checkChainId( std::numeric_limits< uint64_t >::max(), false ); } BOOST_AUTO_TEST_CASE( TransactionChainIDBiggerThan64Bit ) { @@ -106,6 +196,28 @@ BOOST_AUTO_TEST_CASE( TransactionChainIDBiggerThan64Bit ) { "ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa08887321be575c8095f789dd4c7" "43dfe42c1820f9231f98a962b210e3ac2452a3" ); BOOST_REQUIRE_THROW( Transaction( txRlp2, CheckTransaction::None ), InvalidSignature ); + + txRlp1 = fromHex( + "0x01f8d9890a0000000000000117808504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b" + "018e0358ac39584bc98a7c979f984b03f85bf85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a000" + "00000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000" + "0000000000000000000000000000000000000780a0e108b83ed5e1b701b249970e61d9ae409eb6870af96f1a9d" + "8827f497375ae5c8a0795eb0b4f36fe712af5e6a8447802c9eb0913a2add86174552bf2e4b0e183feb" ); + RLPStream rlpStream; + auto tx = Transaction( txRlp1, CheckTransaction::None, false, true ); + auto txBytes = tx.toBytes(IncludeSignature::WithSignature); + BOOST_REQUIRE( txBytes != txRlp1 ); + + txRlp1 = fromHex( + "0x02f8df890a0000000000000117808504a817c8008504a817c800827530947d36af85a184e220a656525fcbb9" + "a63b9ab3c12b018e0358ac39584bc98a7c979f984b03f85bf85994de0b295669a9fd93d5f28d9ec85e40f4cb69" + "7baef842a00000000000000000000000000000000000000000000000000000000000000003a000000000000000" + "0000000000000000000000000000000000000000000000000780a0912e3aad5af05008d3a282d2a76dc975d234" + "4eb34e2500c924a58ccfdc9dbeb4a04afcffcb5d1897df030d45a7eeb3ceb7c7e6fe368fc47865156b4899de32" + "01c7" ); + tx = Transaction( txRlp1, CheckTransaction::None, false, true ); + txBytes = tx.toBytes(IncludeSignature::WithSignature); + BOOST_REQUIRE( txBytes != txRlp1 ); } BOOST_AUTO_TEST_CASE( TransactionReplayProtected ) { @@ -117,9 +229,69 @@ BOOST_AUTO_TEST_CASE( TransactionReplayProtected ) { tx.checkChainId( 1, false ); BOOST_REQUIRE_THROW( tx.checkChainId( 123, false ), InvalidSignature ); - RLPStream txRlpStream; - tx.streamRLP( txRlpStream ); - BOOST_REQUIRE( txRlpStream.out() == txRlp ); + auto txBytes = tx.toBytes(IncludeSignature::WithSignature); + BOOST_REQUIRE( txBytes == txRlp ); + + txRlp = fromHex( + "0x01f8c38197018504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b0180f85bf85994de" + "0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000" + "000000000000000003a0000000000000000000000000000000000000000000000000000000000000000780a0b0" + "3eaf481958e22fc39bd1d526eb9255be1e6625614f02ca939e51c3d7e64bcaa05f675640c04bb050d27bd1f39c" + "07b6ff742311b04dab760bb3bc206054332879" ); + tx = Transaction( txRlp, CheckTransaction::None, false, true ); + tx.checkChainId( 151, false ); + BOOST_REQUIRE_THROW( tx.checkChainId( 123, false ), InvalidSignature ); + + BOOST_REQUIRE( tx.toBytes() == txRlp ); + + txRlp = fromHex( + "0x02f8c98197808504a817c8008504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b0180" + "f85bf85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000" + "000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000" + "00000780a0f1a407dfc1a9f782001d89f617e9b3a2f295378533784fb39960dea60beea2d0a05ac3da2946554b" + "a3d5721850f4f89ee7a0c38e4acab7130908e7904d13174388" ); + tx = Transaction( txRlp, CheckTransaction::None, false, true ); + tx.checkChainId( 151, false ); + BOOST_REQUIRE_THROW( tx.checkChainId( 123, false ), InvalidSignature ); + + BOOST_REQUIRE( tx.toBytes() == txRlp ); +} + +BOOST_AUTO_TEST_CASE( accessList ) { + // [ { 'address': HexBytes( "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae" ), + // 'storageKeys': ( "0x0000000000000000000000000000000000000000000000000000000000000003", "0x0000000000000000000000000000000000000000000000000000000000000007" ) } ] + auto txRlp = fromHex( + "0x01f8c38197018504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b0180f85bf85994de" + "0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000" + "000000000000000003a0000000000000000000000000000000000000000000000000000000000000000780a0b0" + "3eaf481958e22fc39bd1d526eb9255be1e6625614f02ca939e51c3d7e64bcaa05f675640c04bb050d27bd1f39c" + "07b6ff742311b04dab760bb3bc206054332879" ); + Transaction tx; + BOOST_REQUIRE_NO_THROW( tx = Transaction( txRlp, CheckTransaction::None, false, true ) ); + BOOST_REQUIRE( tx.accessList().size() == 1 ); + + // empty accessList + txRlp = fromHex( + "0x01f8678197808504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b0180c001a01ebdc5" + "46c8b85511b7ba831f47c4981069d7af972d10b7dce2c57225cb5df6a7a055ae1e84fea41d37589eb740a0a930" + "17a5cd0e9f10ee50f165bf4b1b4c78ddae" ); + BOOST_REQUIRE_NO_THROW( tx = Transaction( txRlp, CheckTransaction::None, false, true ) ); + BOOST_REQUIRE( tx.accessList().size() == 0 ); + + // no accessList + txRlp = fromHex( + "0x01f8678197808504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b0180c080a025fffe" + "aafed61a15aefd1be5ccbd19e3fe07d0088b06ab6ad960d0f6c382d8cea02e255bf1a7de0a75ccec6d00bcc367" + "1af06ca9641fc02024a9d6b28f9b01307b" ); + BOOST_REQUIRE_NO_THROW( tx = Transaction( txRlp, CheckTransaction::None, false, true ) ); + BOOST_REQUIRE( tx.accessList().size() == 0 ); + + // change empty accessList 0xc0 to empty array 0x80 + txRlp = fromHex( + "0x01f8678197808504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b01808001a01ebdc5" + "46c8b85511b7ba831f47c4981069d7af972d10b7dce2c57225cb5df6a7a055ae1e84fea41d37589eb740a0a930" + "17a5cd0e9f10ee50f165bf4b1b4c78ddae" ); + BOOST_REQUIRE_THROW( Transaction( txRlp, CheckTransaction::None, false, true ), InvalidTransactionFormat ); } BOOST_AUTO_TEST_CASE( ExecutionResultOutput, @@ -290,9 +462,8 @@ BOOST_AUTO_TEST_CASE( GettingSignatureForUnsignedTransactionThrows, BOOST_AUTO_TEST_CASE( StreamRLPWithSignatureForUnsignedTransactionThrows ) { Transaction tx( 0, 0, 10000, Address( "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" ), bytes(), 0 ); - RLPStream s; BOOST_REQUIRE_THROW( - tx.streamRLP( s, IncludeSignature::WithSignature, false ), TransactionIsUnsigned ); + tx.toBytes( IncludeSignature::WithSignature ), TransactionIsUnsigned ); } BOOST_AUTO_TEST_CASE( CheckLowSForUnsignedTransactionThrows, diff --git a/test/unittests/libethereum/TransactionQueue.cpp b/test/unittests/libethereum/TransactionQueue.cpp index 2036e9520..ee0dd8762 100644 --- a/test/unittests/libethereum/TransactionQueue.cpp +++ b/test/unittests/libethereum/TransactionQueue.cpp @@ -234,16 +234,16 @@ BOOST_AUTO_TEST_CASE( tqImport ) { TransactionQueue tq; h256Hash known = tq.knownTransactions(); BOOST_REQUIRE( known.size() == 0 ); - - ImportResult ir = tq.import( testTransaction.transaction().rlp() ); + + ImportResult ir = tq.import( testTransaction.transaction().toBytes() ); BOOST_REQUIRE( ir == ImportResult::Success ); known = tq.knownTransactions(); BOOST_REQUIRE( known.size() == 1 ); - - ir = tq.import( testTransaction.transaction().rlp() ); + + ir = tq.import( testTransaction.transaction().toBytes() ); BOOST_REQUIRE( ir == ImportResult::AlreadyKnown ); - - bytes rlp = testTransaction.transaction().rlp(); + + bytes rlp = testTransaction.transaction().toBytes(); rlp.at( 0 ) = 03; ir = tq.import( rlp ); BOOST_REQUIRE( ir == ImportResult::Malformed ); @@ -254,14 +254,14 @@ BOOST_AUTO_TEST_CASE( tqImport ) { TestTransaction testTransaction2 = TestTransaction::defaultTransaction( 1, 2 ); TestTransaction testTransaction3 = TestTransaction::defaultTransaction( 1, 1 ); TestTransaction testTransaction4 = TestTransaction::defaultTransaction( 1, 4 ); - - ir = tq.import( testTransaction2.transaction().rlp() ); + + ir = tq.import( testTransaction2.transaction().toBytes() ); BOOST_REQUIRE( ir == ImportResult::SameNonceAlreadyInQueue ); - - ir = tq.import( testTransaction3.transaction().rlp() ); + + ir = tq.import( testTransaction3.transaction().toBytes() ); BOOST_REQUIRE( ir == ImportResult::AlreadyKnown ); - - ir = tq.import( testTransaction4.transaction().rlp() ); + + ir = tq.import( testTransaction4.transaction().toBytes() ); known = tq.knownTransactions(); BOOST_REQUIRE( known.size() == 1 ); Transactions ts = tq.topTransactions( 4 ); @@ -289,8 +289,8 @@ BOOST_AUTO_TEST_CASE( tqImportFuture ) { BOOST_REQUIRE( maxNonce == 0 ); u256 waiting = tq.waiting(sender); BOOST_REQUIRE( waiting == 0 ); - - ImportResult ir1 = tq.import( tx1.transaction().rlp(), IfDropped::Ignore, true ); + + ImportResult ir1 = tq.import( tx1.transaction().toBytes(), IfDropped::Ignore, true ); BOOST_REQUIRE( ir1 == ImportResult::Success ); known = tq.knownTransactions(); BOOST_REQUIRE( known.size() == 1 ); @@ -302,7 +302,7 @@ BOOST_AUTO_TEST_CASE( tqImportFuture ) { BOOST_REQUIRE( waiting == 1 ); // HACK it's now allowed to repeat future transaction (can put it to current) - ir1 = tq.import( tx1.transaction().rlp(), IfDropped::Ignore, true ); + ir1 = tq.import( tx1.transaction().toBytes(), IfDropped::Ignore, true ); BOOST_REQUIRE( ir1 == ImportResult::Success ); known = tq.knownTransactions(); BOOST_REQUIRE( known.size() == 1 ); @@ -312,14 +312,14 @@ BOOST_AUTO_TEST_CASE( tqImportFuture ) { BOOST_REQUIRE( maxNonce == 5 ); waiting = tq.waiting(sender); BOOST_REQUIRE( waiting == 1 ); - - bytes rlp = tx1.transaction().rlp(); + + bytes rlp = tx1.transaction().toBytes(); rlp.at( 0 ) = 03; ir1 = tq.import( rlp, IfDropped::Ignore, true ); BOOST_REQUIRE( ir1 == ImportResult::Malformed ); TestTransaction tx2 = TestTransaction::defaultTransaction(2); - ImportResult ir2 = tq.import( tx2.transaction().rlp(), IfDropped::Ignore, true ); + ImportResult ir2 = tq.import( tx2.transaction().toBytes(), IfDropped::Ignore, true ); BOOST_REQUIRE( ir2 == ImportResult::Success ); known = tq.knownTransactions(); BOOST_REQUIRE( known.size() == 2 ); @@ -330,7 +330,7 @@ BOOST_AUTO_TEST_CASE( tqImportFuture ) { BOOST_CHECK( ( Transactions{} ) == tq.topTransactions( 256 ) ); TestTransaction tx3 = TestTransaction::defaultTransaction(1); - ImportResult ir3 = tq.import( tx3.transaction().rlp(), IfDropped::Ignore, true ); + ImportResult ir3 = tq.import( tx3.transaction().toBytes(), IfDropped::Ignore, true ); BOOST_REQUIRE( ir3 == ImportResult::Success ); known = tq.knownTransactions(); BOOST_REQUIRE( known.size() == 3 ); @@ -341,7 +341,7 @@ BOOST_AUTO_TEST_CASE( tqImportFuture ) { BOOST_CHECK( ( Transactions{} ) == tq.topTransactions( 256 ) ); TestTransaction tx4 = TestTransaction::defaultTransaction(0); - ImportResult ir4 = tq.import( tx4.transaction().rlp(), IfDropped::Ignore ); + ImportResult ir4 = tq.import( tx4.transaction().toBytes(), IfDropped::Ignore ); BOOST_REQUIRE( ir4 == ImportResult::Success ); known = tq.knownTransactions(); BOOST_REQUIRE( known.size() == 4 ); @@ -355,7 +355,7 @@ BOOST_AUTO_TEST_CASE( tqImportFuture ) { BOOST_CHECK( ( Transactions{ tx4.transaction(), tx3.transaction(), tx2.transaction() } ) == tq.topTransactions( 256 ) ); TestTransaction tx5 = TestTransaction::defaultTransaction(3); - ImportResult ir5 = tq.import( tx5.transaction().rlp(), IfDropped::Ignore ); + ImportResult ir5 = tq.import( tx5.transaction().toBytes(), IfDropped::Ignore ); BOOST_REQUIRE( ir5 == ImportResult::Success ); known = tq.knownTransactions(); BOOST_REQUIRE( known.size() == 5 ); @@ -387,13 +387,13 @@ BOOST_AUTO_TEST_CASE( dropFromFutureToCurrent ) { TestTransaction tx1 = TestTransaction::defaultTransaction(1); // put transaction to future - ImportResult ir1 = tq.import( tx1.transaction().rlp(), IfDropped::Ignore, true ); + ImportResult ir1 = tq.import( tx1.transaction().toBytes(), IfDropped::Ignore, true ); BOOST_REQUIRE( ir1 == ImportResult::Success ); TransactionQueue::Status status = tq.status(); BOOST_REQUIRE( status.current == 0 && status.future == 1 ); // push it to current and see it fall back from future to current - ir1 = tq.import( tx1.transaction().rlp(), IfDropped::Ignore, false ); + ir1 = tq.import( tx1.transaction().toBytes(), IfDropped::Ignore, false ); BOOST_REQUIRE( ir1 == ImportResult::Success ); status = tq.status(); BOOST_REQUIRE( status.current == 1 && status.future == 0 ); @@ -402,10 +402,10 @@ BOOST_AUTO_TEST_CASE( dropFromFutureToCurrent ) { BOOST_AUTO_TEST_CASE( tqImportFutureLimits ) { dev::eth::TransactionQueue tq( 1024, 2 ); TestTransaction tx1 = TestTransaction::defaultTransaction(3); - tq.import( tx1.transaction().rlp(), IfDropped::Ignore, true ); + tq.import( tx1.transaction().toBytes(), IfDropped::Ignore, true ); TestTransaction tx2 = TestTransaction::defaultTransaction(2); - tq.import( tx2.transaction().rlp(), IfDropped::Ignore, true ); + tq.import( tx2.transaction().toBytes(), IfDropped::Ignore, true ); auto waiting = tq.waiting(tx1.transaction().sender()); BOOST_REQUIRE( waiting == 2 ); @@ -413,7 +413,7 @@ BOOST_AUTO_TEST_CASE( tqImportFutureLimits ) { BOOST_REQUIRE( known.size() == 2 ); TestTransaction tx3 = TestTransaction::defaultTransaction(1); - ImportResult ir = tq.import( tx3.transaction().rlp(), IfDropped::Ignore, true ); + ImportResult ir = tq.import( tx3.transaction().toBytes(), IfDropped::Ignore, true ); BOOST_REQUIRE( ir == ImportResult::Success ); waiting = tq.waiting(tx1.transaction().sender()); @@ -427,7 +427,7 @@ BOOST_AUTO_TEST_CASE( tqImportFutureLimits2 ) { dev::eth::TransactionQueue tq( 1024, 2 ); TestTransaction tx1 = TestTransaction::defaultTransaction(3); - tq.import( tx1.transaction().rlp(), IfDropped::Ignore, true ); + tq.import( tx1.transaction().toBytes(), IfDropped::Ignore, true ); auto waiting = tq.waiting(tx1.transaction().sender()); BOOST_REQUIRE( waiting == 1 ); @@ -454,7 +454,7 @@ BOOST_AUTO_TEST_CASE( tqImportFutureLimits2 ) { BOOST_REQUIRE( status.future == 2 ); TestTransaction tx2 = TestTransaction::defaultTransaction(2); - tq.import( tx2.transaction().rlp(), IfDropped::Ignore, true ); + tq.import( tx2.transaction().toBytes(), IfDropped::Ignore, true ); waiting = tq.waiting(tx1.transaction().sender()); BOOST_REQUIRE( waiting == 1 ); @@ -472,7 +472,7 @@ BOOST_AUTO_TEST_CASE( tqDrop ) { TransactionQueue tq; TestTransaction testTransaction = TestTransaction::defaultTransaction(); tq.dropGood( testTransaction.transaction() ); - tq.import( testTransaction.transaction().rlp() ); + tq.import( testTransaction.transaction().toBytes() ); BOOST_REQUIRE( tq.topTransactions( 4 ).size() == 1 ); tq.dropGood( testTransaction.transaction() ); BOOST_REQUIRE( tq.topTransactions( 4 ).size() == 0 ); @@ -516,8 +516,8 @@ BOOST_AUTO_TEST_CASE( tqLimit ) { BOOST_AUTO_TEST_CASE( tqLimitBytes ) { TransactionQueue tq( 100, 100, 250, 250 ); - - unsigned maxTxCount = 250 / TestTransaction::defaultTransaction( 1 ).transaction().rlp().size(); + + unsigned maxTxCount = 250 / TestTransaction::defaultTransaction( 1 ).transaction().toBytes().size(); TestTransaction testTransaction = TestTransaction::defaultTransaction( 2 ); ImportResult res = tq.import( testTransaction.transaction(), IfDropped::Ignore, true ); @@ -554,8 +554,8 @@ BOOST_AUTO_TEST_CASE( tqLimitBytes ) { BOOST_AUTO_TEST_CASE( tqEqueue ) { TransactionQueue tq; TestTransaction testTransaction = TestTransaction::defaultTransaction(); - - bytes payloadToDecode = testTransaction.transaction().rlp(); + + bytes payloadToDecode = testTransaction.transaction().toBytes(); RLPStream rlpStream( 2 ); rlpStream.appendRaw( payloadToDecode ); diff --git a/test/unittests/libevm/VMTest.cpp b/test/unittests/libevm/VMTest.cpp index fedb0e35c..9edcf9aeb 100644 --- a/test/unittests/libevm/VMTest.cpp +++ b/test/unittests/libevm/VMTest.cpp @@ -17,6 +17,7 @@ along with cpp-ethereum. If not, see . */ +#include #include #include #include @@ -55,7 +56,7 @@ class Create2TestFixture : public TestOutputHelperFixture { virtual ~Create2TestFixture() { state.releaseWriteLock(); } void testCreate2worksInConstantinople() { - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, ref( inputData ), ref( code ), sha3( code ), version, depth, isCreate, staticCall ); vm->exec( gas, extVm, OnOpFunc{} ); @@ -66,7 +67,7 @@ class Create2TestFixture : public TestOutputHelperFixture { void testCreate2isInvalidBeforeConstantinople() { se.reset( ChainParams( genesisInfo( Network::ByzantiumTest ) ).createSealEngine() ); - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, ref( inputData ), ref( code ), sha3( code ), version, depth, isCreate, staticCall ); BOOST_REQUIRE_THROW( vm->exec( gas, extVm, OnOpFunc{} ), BadInstruction ); @@ -75,7 +76,7 @@ class Create2TestFixture : public TestOutputHelperFixture { void testCreate2succeedsIfAddressHasEther() { state.addBalance( expectedAddress, 1 * ether ); - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, ref( inputData ), ref( code ), sha3( code ), version, depth, isCreate, staticCall ); vm->exec( gas, extVm, OnOpFunc{} ); @@ -86,7 +87,7 @@ class Create2TestFixture : public TestOutputHelperFixture { void testCreate2doesntChangeContractIfAddressExists() { state.setCode( expectedAddress, bytes{inputData}, 0 ); - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, ref( inputData ), ref( code ), sha3( code ), version, depth, isCreate, staticCall ); vm->exec( gas, extVm, OnOpFunc{} ); @@ -96,7 +97,7 @@ class Create2TestFixture : public TestOutputHelperFixture { void testCreate2isForbiddenInStaticCall() { staticCall = true; - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, ref( inputData ), ref( code ), sha3( code ), version, depth, isCreate, staticCall ); BOOST_REQUIRE_THROW( vm->exec( gas, extVm, OnOpFunc{} ), DisallowedStateChange ); @@ -109,7 +110,7 @@ class Create2TestFixture : public TestOutputHelperFixture { state.createContract( expectedAddress ); state.setStorage( expectedAddress, 1, 1 ); - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, ref( inputData ), ref( code ), sha3( code ), version, depth, isCreate, staticCall ); vm->exec( gas, extVm, OnOpFunc{} ); @@ -127,7 +128,7 @@ class Create2TestFixture : public TestOutputHelperFixture { state.createContract( expectedAddress ); state.setStorage( expectedAddress, 1, 1 ); - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, ref( inputData ), ref( code ), sha3( code ), version, depth, isCreate, staticCall ); vm->exec( gas, extVm, OnOpFunc{} ); @@ -136,7 +137,7 @@ class Create2TestFixture : public TestOutputHelperFixture { } void testCreate2costIncludesInitCodeHashing() { - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, ref( inputData ), ref( code ), sha3( code ), version, depth, isCreate, staticCall ); uint64_t gasBefore = 0; @@ -174,7 +175,7 @@ class Create2TestFixture : public TestOutputHelperFixture { State state = State( 0 ).createStateModifyCopy(); std::unique_ptr< SealEngineFace > se{ ChainParams( genesisInfo( Network::ConstantinopleTest ) ).createSealEngine()}; - EnvInfo envInfo{blockHeader, lastBlockHashes, 0, se->chainParams().chainID}; + EnvInfo envInfo{blockHeader, lastBlockHashes, 1, 0, se->chainParams().chainID}; u256 value = 0; u256 gasPrice = 1; @@ -219,7 +220,7 @@ class ExtcodehashTestFixture : public TestOutputHelperFixture { } void testExtcodehashWorksInConstantinople() { - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, extAddress.ref(), ref( code ), sha3( code ), version, depth, isCreate, staticCall ); owning_bytes_ref ret = vm->exec( gas, extVm, OnOpFunc{} ); @@ -228,7 +229,7 @@ class ExtcodehashTestFixture : public TestOutputHelperFixture { } void testExtcodehashHasCorrectCost() { - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, extAddress.ref(), ref( code ), sha3( code ), version, depth, isCreate, staticCall ); bigint gasBefore; @@ -250,7 +251,7 @@ class ExtcodehashTestFixture : public TestOutputHelperFixture { void testExtCodeHashisInvalidBeforeConstantinople() { se.reset( ChainParams( genesisInfo( Network::ByzantiumTest ) ).createSealEngine() ); - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, extAddress.ref(), ref( code ), sha3( code ), version, depth, isCreate, staticCall ); BOOST_REQUIRE_THROW( vm->exec( gas, extVm, OnOpFunc{} ), BadInstruction ); @@ -260,7 +261,7 @@ class ExtcodehashTestFixture : public TestOutputHelperFixture { Address addressWithEmptyCode{KeyPair::create().address()}; state.addBalance( addressWithEmptyCode, 1 * ether ); - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, addressWithEmptyCode.ref(), ref( code ), sha3( code ), version, depth, isCreate, staticCall ); @@ -273,7 +274,7 @@ class ExtcodehashTestFixture : public TestOutputHelperFixture { void testExtCodeHashOfNonExistentAccount() { Address addressNonExisting{0x1234}; - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, addressNonExisting.ref(), ref( code ), sha3( code ), version, depth, isCreate, staticCall ); @@ -285,7 +286,7 @@ class ExtcodehashTestFixture : public TestOutputHelperFixture { void testExtCodeHashOfPrecomileZeroBalance() { Address addressPrecompile{0x1}; - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, addressPrecompile.ref(), ref( code ), sha3( code ), version, depth, isCreate, staticCall ); @@ -298,7 +299,7 @@ class ExtcodehashTestFixture : public TestOutputHelperFixture { Address addressPrecompile{0x1}; state.addBalance( addressPrecompile, 1 * ether ); - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, addressPrecompile.ref(), ref( code ), sha3( code ), version, depth, isCreate, staticCall ); @@ -319,7 +320,7 @@ class ExtcodehashTestFixture : public TestOutputHelperFixture { bytes extAddressPrefixed = bytes{1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc} + extAddress.ref(); - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, ref( extAddressPrefixed ), ref( code ), sha3( code ), version, depth, isCreate, staticCall ); @@ -335,7 +336,7 @@ class ExtcodehashTestFixture : public TestOutputHelperFixture { State state{0}; std::unique_ptr< SealEngineFace > se{ ChainParams( genesisInfo( Network::ConstantinopleTest ) ).createSealEngine()}; - EnvInfo envInfo{blockHeader, lastBlockHashes, 0, se->chainParams().chainID}; + EnvInfo envInfo{blockHeader, lastBlockHashes, 1, 0, se->chainParams().chainID}; u256 value = 0; u256 gasPrice = 1; @@ -425,7 +426,7 @@ class SstoreTestFixture : public TestOutputHelperFixture { state.commit( dev::eth::CommitBehaviour::RemoveEmptyAccounts ); bytes const code = fromHex( _codeStr ); - ExtVM extVm( state, envInfo, *se, to, from, from, value, gasPrice, inputData, ref( code ), + ExtVM extVm( state, envInfo, se->chainParams(), to, from, from, value, gasPrice, inputData, ref( code ), sha3( code ), version, depth, isCreate, staticCall ); u256 gasBefore = gas; @@ -443,7 +444,7 @@ class SstoreTestFixture : public TestOutputHelperFixture { State state = State( 0 ).createStateModifyCopy(); std::unique_ptr< SealEngineFace > se{ ChainParams( genesisInfo( Network::ConstantinopleTest ) ).createSealEngine()}; - EnvInfo envInfo{blockHeader, lastBlockHashes, 0, se->chainParams().chainID}; + EnvInfo envInfo{blockHeader, lastBlockHashes, 1, 0, se->chainParams().chainID}; u256 value = 0; u256 gasPrice = 1; @@ -472,7 +473,7 @@ class ChainIDTestFixture : public TestOutputHelperFixture { explicit ChainIDTestFixture( VMFace* _vm ) : vm{_vm} { state.addBalance( address, 1 * ether ); } void testChainIDWorksInIstanbul() { - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, {}, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, {}, ref( code ), sha3( code ), version, depth, isCreate, staticCall ); owning_bytes_ref ret = vm->exec( gas, extVm, OnOpFunc{} ); @@ -481,7 +482,7 @@ class ChainIDTestFixture : public TestOutputHelperFixture { } void testChainIDHasCorrectCost() { - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, {}, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, {}, ref( code ), sha3( code ), version, depth, isCreate, staticCall ); bigint gasBefore; @@ -504,7 +505,7 @@ class ChainIDTestFixture : public TestOutputHelperFixture { se.reset( ChainParams( genesisInfo( Network::ConstantinopleFixTest ) ).createSealEngine() ); version = ConstantinopleFixSchedule.accountVersion; - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, {}, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, {}, ref( code ), sha3( code ), version, depth, isCreate, staticCall ); BOOST_REQUIRE_THROW( vm->exec( gas, extVm, OnOpFunc{} ), BadInstruction ); @@ -517,7 +518,7 @@ class ChainIDTestFixture : public TestOutputHelperFixture { State state{0}; std::unique_ptr< SealEngineFace > se{ ChainParams( genesisInfo( Network::IstanbulTest ) ).createSealEngine()}; - EnvInfo envInfo{blockHeader, lastBlockHashes, 0, se->chainParams().chainID}; + EnvInfo envInfo{blockHeader, lastBlockHashes, 1, 0, se->chainParams().chainID}; u256 value = 0; u256 gasPrice = 1; @@ -551,14 +552,14 @@ class BalanceFixture : public TestOutputHelperFixture { explicit BalanceFixture( VMFace* _vm ) : vm{_vm} { state.addBalance( address, 1 * ether ); } void testSelfBalanceWorksInIstanbul() { - ExtVM extVmSelfBalance( state, envInfo, *se, address, address, address, value, gasPrice, {}, + ExtVM extVmSelfBalance( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, {}, ref( codeSelfBalance ), sha3( codeSelfBalance ), version, depth, isCreate, staticCall ); owning_bytes_ref retSelfBalance = vm->exec( gas, extVmSelfBalance, OnOpFunc{} ); BOOST_REQUIRE_EQUAL( fromBigEndian< u256 >( retSelfBalance ), 1 * ether ); - ExtVM extVmBalance( state, envInfo, *se, address, address, address, value, gasPrice, {}, + ExtVM extVmBalance( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, {}, ref( codeBalance ), sha3( codeBalance ), version, depth, isCreate, staticCall ); owning_bytes_ref retBalance = vm->exec( gas, extVmBalance, OnOpFunc{} ); @@ -568,7 +569,7 @@ class BalanceFixture : public TestOutputHelperFixture { } void testSelfBalanceHasCorrectCost() { - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, {}, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, {}, ref( codeSelfBalance ), sha3( codeSelfBalance ), version, depth, isCreate, staticCall ); bigint gasBefore; @@ -588,7 +589,7 @@ class BalanceFixture : public TestOutputHelperFixture { } void testBalanceHasCorrectCost() { - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, {}, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, {}, ref( codeBalance ), sha3( codeBalance ), version, depth, isCreate, staticCall ); bigint gasBefore; @@ -611,7 +612,7 @@ class BalanceFixture : public TestOutputHelperFixture { se.reset( ChainParams( genesisInfo( Network::ConstantinopleFixTest ) ).createSealEngine() ); version = ConstantinopleFixSchedule.accountVersion; - ExtVM extVm( state, envInfo, *se, address, address, address, value, gasPrice, {}, + ExtVM extVm( state, envInfo, se->chainParams(), address, address, address, value, gasPrice, {}, ref( codeSelfBalance ), sha3( codeSelfBalance ), version, depth, isCreate, staticCall ); BOOST_REQUIRE_THROW( vm->exec( gas, extVm, OnOpFunc{} ), BadInstruction ); @@ -624,7 +625,7 @@ class BalanceFixture : public TestOutputHelperFixture { State state{0}; std::unique_ptr< SealEngineFace > se{ ChainParams( genesisInfo( Network::IstanbulTest ) ).createSealEngine()}; - EnvInfo envInfo{blockHeader, lastBlockHashes, 0, se->chainParams().chainID}; + EnvInfo envInfo{blockHeader, lastBlockHashes, 1, 0, se->chainParams().chainID}; u256 value = 0; u256 gasPrice = 1; @@ -648,6 +649,47 @@ class BalanceFixture : public TestOutputHelperFixture { std::unique_ptr< VMFace > vm; }; +class InstructionTestFixture : public TestOutputHelperFixture { +public: + InstructionTestFixture() : vm{new LegacyVM()} { + ChainParams cp( genesisInfo( Network::IstanbulTest ) ); + cp.sChain._patchTimestamps[static_cast(SchainPatchEnum::PushZeroPatch)] = 1; + SchainPatch::init(cp); + + se.reset(cp.createSealEngine()); + envInfo = std::make_unique ( blockHeader, lastBlockHashes, 1, 0, cp.chainID ); + + state.addBalance( address, 1 * ether ); + } + + void testCode( std::string const& _codeStr ) { + + bytes const code = fromHex( _codeStr ); + + ExtVM extVm( state, *envInfo, se->chainParams(), address, address, address, value, gasPrice, {}, + ref( code ), sha3( code ), version, depth, isCreate, staticCall ); + + owning_bytes_ref ret = vm->exec( gas, extVm, OnOpFunc{} ); + } + + BlockHeader blockHeader{initBlockHeader()}; + LastBlockHashes lastBlockHashes; + Address address{KeyPair::create().address()}; + State state{0}; + std::unique_ptr< SealEngineFace > se; + std::unique_ptr envInfo; + + u256 value = 0; + u256 gasPrice = 1; + u256 version = IstanbulSchedule.accountVersion; + int depth = 0; + bool isCreate = false; + bool staticCall = false; + u256 gas = 1000000; + + std::unique_ptr< LegacyVM > vm; +}; + class LegacyVMBalanceFixture : public BalanceFixture { public: LegacyVMBalanceFixture() : BalanceFixture{new LegacyVM} {} @@ -857,6 +899,18 @@ BOOST_AUTO_TEST_CASE( LegacyVMSelfBalanceisInvalidBeforeIstanbul, } BOOST_AUTO_TEST_SUITE_END() +BOOST_FIXTURE_TEST_SUITE( InstructionSuite, InstructionTestFixture ) + +BOOST_AUTO_TEST_CASE( Push0 ) { + string code = "5f"; + BOOST_REQUIRE_NO_THROW( this->testCode(code) ); + u256s stack = vm->stack(); + BOOST_REQUIRE_EQUAL(stack.size(), 1); + BOOST_REQUIRE_EQUAL(stack[0], u256()); +} + +BOOST_AUTO_TEST_SUITE_END() + BOOST_AUTO_TEST_SUITE_END() BOOST_FIXTURE_TEST_SUITE( SkaleInterpreterSuite, TestOutputHelperFixture ) diff --git a/test/unittests/libweb3jsonrpc/WebThreeStubClient.cpp b/test/unittests/libweb3jsonrpc/WebThreeStubClient.cpp index 612b934fa..6891aacef 100644 --- a/test/unittests/libweb3jsonrpc/WebThreeStubClient.cpp +++ b/test/unittests/libweb3jsonrpc/WebThreeStubClient.cpp @@ -810,6 +810,51 @@ bool WebThreeStubClient::eth_notePassword( const std::string& param1 ) { jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString() ); } +Json::Value WebThreeStubClient::eth_pendingTransactions() { + Json::Value p; + Json::Value result = this->CallMethod( "eth_pendingTransactions", p ); + if ( result.isArray() ) + return result; + else + throw jsonrpc::JsonRpcException( + jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString() ); +} + +std::string WebThreeStubClient::eth_maxPriorityFeePerGas() { + Json::Value p; + Json::Value result = this->CallMethod( "eth_maxPriorityFeePerGas", p ); + if ( result.isString() ) + return result.asString(); + else + throw jsonrpc::JsonRpcException( + jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString() ); +} + +Json::Value WebThreeStubClient::eth_createAccessList( const Json::Value& param1, const std::string& param2 ) { + Json::Value p; + p.append( param1 ); + p.append( param2 ); + Json::Value result = this->CallMethod( "eth_createAccessList", p ); + if ( result.isObject() ) + return result; + else + throw jsonrpc::JsonRpcException( + jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString() ); +} + +Json::Value WebThreeStubClient::eth_feeHistory( const std::string& param1, const std::string& param2, const Json::Value& param3 ) { + Json::Value p; + p.append( param1 ); + p.append( param2 ); + p.append( param3 ); + Json::Value result = this->CallMethod( "eth_feeHistory", p ); + if ( result.isObject() ) + return result; + else + throw jsonrpc::JsonRpcException( + jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString() ); +} + bool WebThreeStubClient::db_put( const std::string& param1, const std::string& param2, const std::string& param3 ) { Json::Value p; diff --git a/test/unittests/libweb3jsonrpc/WebThreeStubClient.h b/test/unittests/libweb3jsonrpc/WebThreeStubClient.h index ab01eceb1..2be6ca519 100644 --- a/test/unittests/libweb3jsonrpc/WebThreeStubClient.h +++ b/test/unittests/libweb3jsonrpc/WebThreeStubClient.h @@ -94,7 +94,11 @@ class WebThreeStubClient : public jsonrpc::Client { Json::Value eth_unsubscribe( const Json::Value& param1 ) noexcept( false ); Json::Value setSchainExitTime( const Json::Value& param1 ) noexcept( false ); Json::Value eth_inspectTransaction( const std::string& param1 ) noexcept( false ); + Json::Value eth_pendingTransactions() noexcept( false ); std::string eth_sendRawTransaction( const std::string& param1 ) noexcept( false ); + std::string eth_maxPriorityFeePerGas() noexcept( false ); + Json::Value eth_createAccessList( const Json::Value& param1, const std::string& param2 ) noexcept( false ); + Json::Value eth_feeHistory( const std::string& param1, const std::string& param2, const Json::Value& param3 ) noexcept( false ); bool eth_notePassword( const std::string& param1 ) noexcept( false ); bool db_put( const std::string& param1, const std::string& param2, const std::string& param3 ) noexcept( false ); diff --git a/test/unittests/libweb3jsonrpc/jsonrpc.cpp b/test/unittests/libweb3jsonrpc/jsonrpc.cpp index 7673c9282..c004aaf6d 100644 --- a/test/unittests/libweb3jsonrpc/jsonrpc.cpp +++ b/test/unittests/libweb3jsonrpc/jsonrpc.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -62,7 +63,7 @@ using namespace dev; using namespace dev::eth; using namespace dev::test; -static size_t rand_port = 1024 + rand() % 64000; +static size_t rand_port = ( srand(time(nullptr)), 1024 + rand() % 64000 ); static std::string const c_genesisConfigString = R"( @@ -100,7 +101,7 @@ static std::string const c_genesisConfigString = "basePort": )"+std::to_string( rand_port ) + R"(, "logLevel": "trace", "logLevelProposal": "trace", - "ecdsaKeyName": "NEK:fa112" + "testSignatures": true }, "sChain": { "schainName": "TestChain", @@ -254,7 +255,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 != "" ) { @@ -293,9 +295,12 @@ JsonRpcFixture( const std::string& _config = "", bool _owner = true, chainParams.sChain.contractStorageLimit = 128; // 615 + 1430 is experimentally-derived block size + average extras size chainParams.sChain.dbStorageLimit = 320.5*( 615 + 1430 ); - chainParams.sChain.contractStoragePatchTimestamp = 1; + chainParams.sChain._patchTimestamps[static_cast(SchainPatchEnum::ContractStoragePatch)] = 1; + chainParams.sChain._patchTimestamps[static_cast(SchainPatchEnum::StorageDestructionPatch)] = 1; powPatchActivationTimestamp = time(nullptr) + 60; - chainParams.sChain.correctForkInPowPatchTimestamp = powPatchActivationTimestamp; // 10 guessed seconds + chainParams.sChain._patchTimestamps[static_cast(SchainPatchEnum::CorrectForkInPowPatch)] = powPatchActivationTimestamp; + push0PatchActivationTimestamp = time(nullptr) + 10; + chainParams.sChain._patchTimestamps[static_cast(SchainPatchEnum::PushZeroPatch)] = push0PatchActivationTimestamp; 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 @@ -303,6 +308,10 @@ JsonRpcFixture( const std::string& _config = "", bool _owner = true, chainParams.extraData = h256::random().asBytes(); 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; @@ -430,6 +439,7 @@ JsonRpcFixture( const std::string& _config = "", bool _owner = true, std::string adminSession; SkaleServerOverride* skale_server_connector; time_t powPatchActivationTimestamp; + time_t push0PatchActivationTimestamp; }; struct RestrictedAddressFixture : public JsonRpcFixture { @@ -825,20 +835,38 @@ BOOST_AUTO_TEST_CASE( simple_contract ) { JsonRpcFixture fixture; dev::eth::simulateMining( *( fixture.client ), 1 ); - + // pragma solidity 0.8.4; // contract test { - // function f(uint a) returns(uint d) { return a * 7; } + // uint value; + // function f(uint a) public pure returns(uint d) { + // return a * 7; + // } + // function setValue(uint _value) external { + // value = _value; + // } // } - string compiled = - "6080604052341561000f57600080fd5b60b98061001d6000396000f300" - "608060405260043610603f576000357c01000000000000000000000000" - "00000000000000000000000000000000900463ffffffff168063b3de64" - "8b146044575b600080fd5b3415604e57600080fd5b606a600480360381" - "019080803590602001909291905050506080565b604051808281526020" - "0191505060405180910390f35b60006007820290509190505600a16562" - "7a7a72305820f294e834212334e2978c6dd090355312a3f0f9476b8eb9" - "8fb480406fc2728a960029"; + string compiled = + "608060405234801561001057600080fd5b506101ef8061002060003" + "96000f3fe608060405234801561001057600080fd5b506004361061" + "00365760003560e01c8063552410771461003b578063b3de648b146" + "10057575b600080fd5b610055600480360381019061005091906100" + "bc565b610087565b005b610071600480360381019061006c9190610" + "0bc565b610091565b60405161007e91906100f4565b604051809103" + "90f35b8060008190555050565b60006007826100a0919061010f565" + "b9050919050565b6000813590506100b6816101a2565b9291505056" + "5b6000602082840312156100ce57600080fd5b60006100dc8482850" + "16100a7565b91505092915050565b6100ee81610169565b82525050" + "565b600060208201905061010960008301846100e5565b929150505" + "65b600061011a82610169565b915061012583610169565b9250817f" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "fffffffff048311821515161561015e5761015d610173565b5b8282" + "02905092915050565b6000819050919050565b7f4e487b710000000" + "0000000000000000000000000000000000000000000000000600052" + "601160045260246000fd5b6101ab81610169565b81146101b657600" + "080fd5b5056fea26469706673582212200be8156151b5ef7c250fa7" + "b8c8ed4e2a1c32cd526f9c868223f6838fa1193c9e64736f6c63430" + "008040033"; Json::Value create; create["code"] = compiled; @@ -865,6 +893,31 @@ BOOST_AUTO_TEST_CASE( simple_contract ) { string result = fixture.rpcClient->eth_call( call, "latest" ); BOOST_CHECK_EQUAL( result, "0x0000000000000000000000000000000000000000000000000000000000000007" ); + + Json::Value inputCall; + inputCall["to"] = contractAddress; + inputCall["input"] = "0xb3de648b0000000000000000000000000000000000000000000000000000000000000001"; + inputCall["gas"] = "1000000"; + inputCall["gasPrice"] = "0"; + result = fixture.rpcClient->eth_call( inputCall, "latest" ); + BOOST_CHECK_EQUAL( + result, "0x0000000000000000000000000000000000000000000000000000000000000007" ); + + Json::Value transact; + transact["to"] = contractAddress; + transact["data"] = "0x552410770000000000000000000000000000000000000000000000000000000000000001"; + txHash = fixture.rpcClient->eth_sendTransaction( transact ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + auto res = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE_EQUAL( res["status"], string( "0x1" ) ); + + Json::Value inputTx; + inputTx["to"] = contractAddress; + inputTx["input"] = "0x552410770000000000000000000000000000000000000000000000000000000000000002"; + txHash = fixture.rpcClient->eth_sendTransaction( inputTx ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + res = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE_EQUAL( res["status"], string( "0x1" ) ); } /* @@ -1272,6 +1325,110 @@ BOOST_AUTO_TEST_CASE( create_opcode ) { BOOST_CHECK( response2 != response1 ); } +BOOST_AUTO_TEST_CASE( push0_patch_activation ) { + JsonRpcFixture fixture; + auto senderAddress = fixture.coinbase.address(); + + fixture.client->setAuthor( senderAddress ); + dev::eth::simulateMining( *( fixture.client ), 1 ); + + fixture.client->setAuthor( fixture.account2.address() ); + dev::eth::simulateMining( *( fixture.client ), 1 ); + +/* +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.8.2; + +contract Push0Test { + fallback() external payable { + assembly { + let t := add(9, 10) + } + } +} + +then convert to yul: solc --ir p0test.sol >p0test.yul + +then change code: + { + let r := add(88,99) + let tmp := verbatim_0i_1o(hex"5f") + } + +then compile! + +*/ + string compiled = + "608060405234156100135761001261003b565b5b61001b610040565b610023610031565b6101b761004382396101b781f35b6000604051905090565b600080fd5b56fe608060405261000f36600061015b565b805160208201f35b60006060905090565b6000604051905090565b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6100738261002a565b810181811067ffffffffffffffff821117156100925761009161003b565b5b80604052505050565b60006100a5610020565b90506100b1828261006a565b919050565b600067ffffffffffffffff8211156100d1576100d061003b565b5b6100da8261002a565b9050602081019050919050565b60006100f2826100b6565b6100fb8161009b565b915082825250919050565b7f7375636365737300000000000000000000000000000000000000000000000000600082015250565b600061013b60076100e7565b905061014960208201610106565b90565b600061015661012f565b905090565b6000610165610017565b809150600a6009015f505061017861014c565b9150509291505056fea2646970667358221220b3871ed09fbcbb1dac74c3cd48dafa5d097bea7c808b5ff2c16a996cf108d3c664736f6c63430008190033"; +// "60806040523415601057600f6031565b5b60166036565b601c6027565b604c60398239604c81f35b6000604051905090565b600080fd5b56fe6080604052600a600c565b005b60636058015f505056fea2646970667358221220ee9861b869ceda6de64f2ec7ccbebf2babce54b35502a866a4193e05ae595e1f64736f6c63430008130033"; + + Json::Value create; + + create["from"] = toJS( senderAddress ); + create["code"] = compiled; + create["gas"] = "1000000"; + + string txHash = fixture.rpcClient->eth_sendTransaction( create ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + + Json::Value receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE_EQUAL( receipt["status"], string( "0x1" ) ); // deploy should succeed + string contractAddress = receipt["contractAddress"].asString(); + + Json::Value callObject; + + callObject["from"] = toJS( fixture.account2.address() ); + callObject["to"] = contractAddress; + + // first try without PushZeroPatch + + txHash = fixture.rpcClient->eth_sendTransaction( callObject ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE_EQUAL( receipt["status"], string( "0x0" ) ); // exec should fail + + string callResult = fixture.rpcClient->eth_call(callObject, "latest"); + BOOST_REQUIRE_EQUAL( callResult, string( "0x" ) ); // call too + + // wait for block after timestamp + BOOST_REQUIRE_LT( fixture.client->blockInfo(LatestBlock).timestamp(), fixture.push0PatchActivationTimestamp ); + while( time(nullptr) < fixture.push0PatchActivationTimestamp ) + sleep(1); + + // 1st timestamp-crossing block + txHash = fixture.rpcClient->eth_sendTransaction( callObject ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + BOOST_REQUIRE_GE( fixture.client->blockInfo(LatestBlock).timestamp(), fixture.push0PatchActivationTimestamp ); + + uint64_t crossingBlockNumber = fixture.client->number(); + (void) crossingBlockNumber; + + // in the "corssing" block tx still should fail + receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE_EQUAL( receipt["status"], string( "0x0" ) ); + + // in 1st block with patch call should succeed + callResult = fixture.rpcClient->eth_call(callObject, "latest"); + BOOST_REQUIRE_NE( callResult, string( "0x" ) ); + + // tx should succeed too + txHash = fixture.rpcClient->eth_sendTransaction( callObject ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE_EQUAL( receipt["status"], string( "0x1" ) ); + +#ifdef HISTORIC_STATE + // histoic call should fail before activation and succees after it + + callResult = fixture.rpcClient->eth_call(callObject, toJS(crossingBlockNumber-1)); + BOOST_REQUIRE_EQUAL( callResult, string( "0x" ) ); + + callResult = fixture.rpcClient->eth_call(callObject, toJS(crossingBlockNumber)); + BOOST_REQUIRE_NE( callResult, string( "0x" ) ); +#endif +} + BOOST_AUTO_TEST_CASE( eth_estimateGas ) { JsonRpcFixture fixture( c_genesisConfigString ); @@ -1958,6 +2115,109 @@ 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) ); +} + +// test blockHash parameter +BOOST_AUTO_TEST_CASE( getLogs_blockHash ) { + JsonRpcFixture fixture; + dev::eth::simulateMining( *( fixture.client ), 1 ); + + string latestHash = fixture.rpcClient->eth_getBlockByNumber("latest", false)["hash"].asString(); + + Json::Value req; + req["blockHash"] = "xyz"; + BOOST_REQUIRE_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req), std::exception ); + + req["blockHash"] = Json::Value(Json::arrayValue); + BOOST_REQUIRE_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req), std::exception ); + + req["fromBlock"] = 1; + req["toBlock"] = 1; + BOOST_REQUIRE_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req), std::exception ); + + req["blockHash"] = latestHash; + BOOST_REQUIRE_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req), std::exception ); + + req.removeMember("fromBlock"); + req.removeMember("toBlock"); + BOOST_REQUIRE_NO_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req) ); + + req["blockHash"] = "0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b"; + BOOST_REQUIRE_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req), std::exception ); + + req["blockHash"] = ""; + BOOST_REQUIRE_THROW( Json::Value logs = fixture.rpcClient->eth_getLogs(req), std::exception ); +} + BOOST_AUTO_TEST_CASE( estimate_gas_low_gas_txn ) { JsonRpcFixture fixture; dev::eth::simulateMining( *( fixture.client ), 10 ); @@ -2012,8 +2272,8 @@ contract TestEstimateGas { dev::bytes data = dev::jsToBytes( estimateGasCall["data"].asString() ); BOOST_REQUIRE( dev::jsToU256( estimatedGas ) > dev::eth::TransactionBase::baseGasRequired( - false, &data, fixture.client->chainParams().scheduleForBlockNumber( - fixture.client->number() ) ) ); + false, &data, fixture.client->chainParams().makeEvmSchedule( + fixture.client->latestBlock().info().timestamp(), fixture.client->number() ) ) ); // try to send with this gas estimateGasCall["gas"] = toJS( jsToInt( estimatedGas ) ); @@ -2614,6 +2874,360 @@ BOOST_AUTO_TEST_CASE( EIP1898Calls ) { } } +BOOST_AUTO_TEST_CASE( eip2930Transactions ) { + std::string _config = c_genesisConfigString; + Json::Value ret; + Json::Reader().parse( _config, ret ); + + // Set chainID = 151 + ret["params"]["chainID"] = "0x97"; + time_t eip1559PatchActivationTimestamp = time(nullptr) + 10; + ret["skaleConfig"]["sChain"]["EIP1559TransactionsPatchTimestamp"] = eip1559PatchActivationTimestamp; + + Json::FastWriter fastWriter; + std::string config = fastWriter.write( ret ); + JsonRpcFixture fixture( config ); + + dev::eth::simulateMining( *( fixture.client ), 20 ); + string senderAddress = toJS(fixture.coinbase.address()); + + Json::Value txRefill; + txRefill["to"] = "0xc868AF52a6549c773082A334E5AE232e0Ea3B513"; + txRefill["from"] = senderAddress; + txRefill["gas"] = "100000"; + txRefill["gasPrice"] = fixture.rpcClient->eth_gasPrice(); + txRefill["value"] = 100000000000000000; + string txHash = fixture.rpcClient->eth_sendTransaction( txRefill ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + + Json::Value receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE( receipt["status"] == string( "0x1" ) ); + BOOST_REQUIRE( receipt["type"] == "0x0" ); + + auto result = fixture.rpcClient->eth_getTransactionByHash( txHash ); + BOOST_REQUIRE( result["type"] == "0x0" ); + BOOST_REQUIRE( !result.isMember( "yParity" ) ); + BOOST_REQUIRE( !result.isMember( "accessList" ) ); + + BOOST_REQUIRE( fixture.rpcClient->eth_getBalance( "0xc868AF52a6549c773082A334E5AE232e0Ea3B513", "latest" ) == "0x16345785d8a0000" ); + + // try sending type1 txn before patchTimestmap + BOOST_REQUIRE_THROW( fixture.rpcClient->eth_sendRawTransaction( "0x01f8678197808504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b0180c001a01ebdc546c8b85511b7ba831f47c4981069d7af972d10b7dce2c57225cb5df6a7a055ae1e84fea41d37589eb740a0a93017a5cd0e9f10ee50f165bf4b1b4c78ddae" ), jsonrpc::JsonRpcException ); // INVALID_PARAMS + sleep( 10 ); + + // force 1 block to update timestamp + txRefill["to"] = "0xc868AF52a6549c773082A334E5AE232e0Ea3B513"; + txRefill["from"] = senderAddress; + txRefill["gas"] = "100000"; + txRefill["gasPrice"] = fixture.rpcClient->eth_gasPrice(); + txRefill["value"] = 0; + txHash = fixture.rpcClient->eth_sendTransaction( txRefill ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE( receipt["status"] == string( "0x1" ) ); + BOOST_REQUIRE( receipt["type"] == "0x0" ); + + // send 1 WEI from 0xc868AF52a6549c773082A334E5AE232e0Ea3B513 to 0x7D36aF85A184E220A656525fcBb9A63B9ab3C12b + // encoded type 1 txn + txHash = fixture.rpcClient->eth_sendRawTransaction( "0x01f8678197808504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b0180c001a01ebdc546c8b85511b7ba831f47c4981069d7af972d10b7dce2c57225cb5df6a7a055ae1e84fea41d37589eb740a0a93017a5cd0e9f10ee50f165bf4b1b4c78ddae" ); + auto pendingTransactions = fixture.rpcClient->eth_pendingTransactions(); + BOOST_REQUIRE( pendingTransactions.isArray() && pendingTransactions.size() == 1); + BOOST_REQUIRE( pendingTransactions[0]["type"] == "0x1" ); + BOOST_REQUIRE( pendingTransactions[0].isMember( "yParity" ) && pendingTransactions[0].isMember( "accessList" ) ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + + // compare with txn hash from geth + BOOST_REQUIRE( txHash == "0xc843560015a655b8f81f65a458be9019bdb5cd8e416b6329ca18f36de0b8244d" ); + + BOOST_REQUIRE( dev::toHexPrefixed( fixture.client->transactions( 4 )[0].toBytes() ) == "0x01f8678197808504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b0180c001a01ebdc546c8b85511b7ba831f47c4981069d7af972d10b7dce2c57225cb5df6a7a055ae1e84fea41d37589eb740a0a93017a5cd0e9f10ee50f165bf4b1b4c78ddae" ); + + BOOST_REQUIRE( fixture.rpcClient->eth_getBalance( "0x7D36aF85A184E220A656525fcBb9A63B9ab3C12b", "latest" ) == "0x1" ); + + auto block = fixture.rpcClient->eth_getBlockByNumber( "4", false ); + BOOST_REQUIRE( block["transactions"].size() == 1 ); + BOOST_REQUIRE( block["transactions"][0].asString() == txHash ); + + block = fixture.rpcClient->eth_getBlockByNumber( "4", true ); + BOOST_REQUIRE( block["transactions"].size() == 1 ); + BOOST_REQUIRE( block["transactions"][0]["hash"].asString() == txHash ); + BOOST_REQUIRE( block["transactions"][0]["type"] == "0x1" ); + BOOST_REQUIRE( toJS( jsToInt( block["transactions"][0]["yParity"].asString() ) + 35 + 2 * fixture.client->chainParams().chainID ) == block["transactions"][0]["v"].asString() ); + BOOST_REQUIRE( block["transactions"][0]["accessList"].isArray() ); + BOOST_REQUIRE( block["transactions"][0]["accessList"].size() == 0 ); + + std::string blockHash = block["hash"].asString(); + BOOST_REQUIRE( fixture.client->transactionHashes( dev::h256( blockHash ) )[0] == dev::h256( "0xc843560015a655b8f81f65a458be9019bdb5cd8e416b6329ca18f36de0b8244d") ); + + receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE( receipt["status"] == string( "0x1" ) ); + BOOST_REQUIRE( receipt["type"] == "0x1" ); + BOOST_REQUIRE( receipt["effectiveGasPrice"] == "0x4a817c800" ); + + result = fixture.rpcClient->eth_getTransactionByHash( txHash ); + BOOST_REQUIRE( result["hash"].asString() == txHash ); + BOOST_REQUIRE( result["type"] == "0x1" ); + BOOST_REQUIRE( toJS( jsToInt( result["yParity"].asString() ) + 35 + 2 * fixture.client->chainParams().chainID ) == result["v"].asString() ); + BOOST_REQUIRE( result["accessList"].isArray() ); + BOOST_REQUIRE( result["accessList"].size() == 0 ); + + result = fixture.rpcClient->eth_getTransactionByBlockHashAndIndex( blockHash, "0x0" ); + BOOST_REQUIRE( result["hash"].asString() == txHash ); + BOOST_REQUIRE( result["type"] == "0x1" ); + BOOST_REQUIRE( toJS( jsToInt( result["yParity"].asString() ) + 35 + 2 * fixture.client->chainParams().chainID ) == result["v"].asString() ); + BOOST_REQUIRE( result["accessList"].isArray() ); + + result = fixture.rpcClient->eth_getTransactionByBlockNumberAndIndex( "0x4", "0x0" ); + BOOST_REQUIRE( result["hash"].asString() == txHash ); + BOOST_REQUIRE( result["type"] == "0x1" ); + BOOST_REQUIRE( toJS( jsToInt( result["yParity"].asString() ) + 35 + 2 * fixture.client->chainParams().chainID ) == result["v"].asString() ); + BOOST_REQUIRE( result["accessList"].isArray() ); + BOOST_REQUIRE( result["accessList"].size() == 0 ); + + // now the same txn with accessList and increased nonce + // [ { 'address': HexBytes( "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae" ), 'storageKeys': ( "0x0000000000000000000000000000000000000000000000000000000000000003", "0x0000000000000000000000000000000000000000000000000000000000000007" ) } ] + txHash = fixture.rpcClient->eth_sendRawTransaction( "0x01f8c38197018504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b0180f85bf85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000780a0b03eaf481958e22fc39bd1d526eb9255be1e6625614f02ca939e51c3d7e64bcaa05f675640c04bb050d27bd1f39c07b6ff742311b04dab760bb3bc206054332879" ); + pendingTransactions = fixture.rpcClient->eth_pendingTransactions(); + BOOST_REQUIRE( pendingTransactions.isArray() && pendingTransactions.size() == 1); + BOOST_REQUIRE( pendingTransactions[0]["type"] == "0x1" ); + BOOST_REQUIRE( pendingTransactions[0].isMember( "yParity" ) && pendingTransactions[0].isMember( "accessList" ) ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + + // compare with txn hash from geth + BOOST_REQUIRE( txHash == "0xa6d3541e06dff71fb8344a4db2a4ad4e0b45024eb23a8f568982b70a5f50f94d" ); + BOOST_REQUIRE( dev::toHexPrefixed( fixture.client->transactions( 5 )[0].toBytes() ) == "0x01f8c38197018504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b0180f85bf85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000780a0b03eaf481958e22fc39bd1d526eb9255be1e6625614f02ca939e51c3d7e64bcaa05f675640c04bb050d27bd1f39c07b6ff742311b04dab760bb3bc206054332879" ); + + result = fixture.rpcClient->eth_getTransactionByHash( txHash ); + BOOST_REQUIRE( result["type"] == "0x1" ); + BOOST_REQUIRE( result["accessList"].isArray() ); + BOOST_REQUIRE( result["accessList"].size() == 1 ); + BOOST_REQUIRE( result["accessList"][0].isObject() && result["accessList"][0].getMemberNames().size() == 2 ); + BOOST_REQUIRE( result["accessList"][0].isMember( "address" ) && result["accessList"][0].isMember( "storageKeys" ) ); + BOOST_REQUIRE( result["accessList"][0]["address"].asString() == "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae" ); + BOOST_REQUIRE( result["accessList"][0]["storageKeys"].isArray() && result["accessList"][0]["storageKeys"].size() == 2 ); + BOOST_REQUIRE( result["accessList"][0]["storageKeys"][0].asString() == "0x0000000000000000000000000000000000000000000000000000000000000003" ); + BOOST_REQUIRE( result["accessList"][0]["storageKeys"][1].asString() == "0x0000000000000000000000000000000000000000000000000000000000000007" ); + + block = fixture.rpcClient->eth_getBlockByNumber( "5", true ); + result = block["transactions"][0]; + BOOST_REQUIRE( result["type"] == "0x1" ); + BOOST_REQUIRE( result["accessList"].isArray() ); + BOOST_REQUIRE( result["accessList"].size() == 1 ); + BOOST_REQUIRE( result["accessList"][0].isObject() && result["accessList"][0].getMemberNames().size() == 2 ); + BOOST_REQUIRE( result["accessList"][0].isMember( "address" ) && result["accessList"][0].isMember( "storageKeys" ) ); + BOOST_REQUIRE( result["accessList"][0]["address"].asString() == "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae" ); + BOOST_REQUIRE( result["accessList"][0]["storageKeys"].isArray() && result["accessList"][0]["storageKeys"].size() == 2 ); + BOOST_REQUIRE( result["accessList"][0]["storageKeys"][0].asString() == "0x0000000000000000000000000000000000000000000000000000000000000003" ); + BOOST_REQUIRE( result["accessList"][0]["storageKeys"][1].asString() == "0x0000000000000000000000000000000000000000000000000000000000000007" ); +} + +BOOST_AUTO_TEST_CASE( eip1559Transactions ) { + std::string _config = c_genesisConfigString; + Json::Value ret; + Json::Reader().parse( _config, ret ); + + // Set chainID = 151 + ret["params"]["chainID"] = "0x97"; + time_t eip1559PatchActivationTimestamp = time(nullptr) + 10; + ret["skaleConfig"]["sChain"]["EIP1559TransactionsPatchTimestamp"] = eip1559PatchActivationTimestamp; + + Json::FastWriter fastWriter; + std::string config = fastWriter.write( ret ); + JsonRpcFixture fixture( config ); + + dev::eth::simulateMining( *( fixture.client ), 20 ); + string senderAddress = toJS(fixture.coinbase.address()); + + Json::Value txRefill; + txRefill["to"] = "0x5EdF1e852fdD1B0Bc47C0307EF755C76f4B9c251"; + txRefill["from"] = senderAddress; + txRefill["gas"] = "100000"; + txRefill["gasPrice"] = fixture.rpcClient->eth_gasPrice(); + txRefill["value"] = 100000000000000000; + string txHash = fixture.rpcClient->eth_sendTransaction( txRefill ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + + Json::Value receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE( receipt["status"] == string( "0x1" ) ); + BOOST_REQUIRE( receipt["type"] == "0x0" ); + + auto result = fixture.rpcClient->eth_getTransactionByHash( txHash ); + BOOST_REQUIRE( result["type"] == "0x0" ); + BOOST_REQUIRE( !result.isMember( "yParity" ) ); + BOOST_REQUIRE( !result.isMember( "accessList" ) ); + + BOOST_REQUIRE( fixture.rpcClient->eth_getBalance( "0x5EdF1e852fdD1B0Bc47C0307EF755C76f4B9c251", "latest" ) == "0x16345785d8a0000" ); + + // try sending type2 txn before patchTimestmap + BOOST_REQUIRE_THROW( fixture.rpcClient->eth_sendRawTransaction( "0x02f8c98197808504a817c8008504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b0180f85bf85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000780a0f1a407dfc1a9f782001d89f617e9b3a2f295378533784fb39960dea60beea2d0a05ac3da2946554ba3d5721850f4f89ee7a0c38e4acab7130908e7904d13174388" ), jsonrpc::JsonRpcException ); // INVALID_PARAMS + sleep( 10 ); + + // force 1 block to update timestamp + txRefill["to"] = "0xc868AF52a6549c773082A334E5AE232e0Ea3B513"; + txRefill["from"] = senderAddress; + txRefill["gas"] = "100000"; + txRefill["gasPrice"] = fixture.rpcClient->eth_gasPrice(); + txRefill["value"] = 0; + txHash = fixture.rpcClient->eth_sendTransaction( txRefill ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE( receipt["status"] == string( "0x1" ) ); + BOOST_REQUIRE( receipt["type"] == "0x0" ); + BOOST_REQUIRE( receipt["effectiveGasPrice"] == "0x4a817c800" ); + + // send 1 WEI from 0x5EdF1e852fdD1B0Bc47C0307EF755C76f4B9c251 to 0x7D36aF85A184E220A656525fcBb9A63B9ab3C12b + // encoded type 2 txn + txHash = fixture.rpcClient->eth_sendRawTransaction( "0x02f8c98197808504a817c8008504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b0180f85bf85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000780a0f1a407dfc1a9f782001d89f617e9b3a2f295378533784fb39960dea60beea2d0a05ac3da2946554ba3d5721850f4f89ee7a0c38e4acab7130908e7904d13174388" ); + auto pendingTransactions = fixture.rpcClient->eth_pendingTransactions(); + BOOST_REQUIRE( pendingTransactions.isArray() && pendingTransactions.size() == 1); + BOOST_REQUIRE( pendingTransactions[0]["type"] == "0x2" ); + BOOST_REQUIRE( pendingTransactions[0].isMember( "yParity" ) && pendingTransactions[0].isMember( "accessList" ) ); + BOOST_REQUIRE( pendingTransactions[0].isMember( "maxFeePerGas" ) && pendingTransactions[0].isMember( "maxPriorityFeePerGas" ) ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + + // compare with txn hash from geth + BOOST_REQUIRE( txHash == "0x7bd586e93e3012577de4ba33e3b887baf520cbb92c5dd10996b262f9c5c8f747" ); + std::cout << dev::toHexPrefixed( fixture.client->transactions( 4 )[0].toBytes() ) << '\n'; + BOOST_REQUIRE( dev::toHexPrefixed( fixture.client->transactions( 4 )[0].toBytes() ) == "0x02f8c98197808504a817c8008504a817c800827530947d36af85a184e220a656525fcbb9a63b9ab3c12b0180f85bf85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000780a0f1a407dfc1a9f782001d89f617e9b3a2f295378533784fb39960dea60beea2d0a05ac3da2946554ba3d5721850f4f89ee7a0c38e4acab7130908e7904d13174388" ); + + BOOST_REQUIRE( fixture.rpcClient->eth_getBalance( "0x7D36aF85A184E220A656525fcBb9A63B9ab3C12b", "latest" ) == "0x1" ); + + auto block = fixture.rpcClient->eth_getBlockByNumber( "4", false ); + BOOST_REQUIRE( block["transactions"].size() == 1 ); + BOOST_REQUIRE( block["transactions"][0].asString() == txHash ); + + block = fixture.rpcClient->eth_getBlockByNumber( "4", true ); + BOOST_REQUIRE( !block["baseFeePerGas"].asString().empty() ); + BOOST_REQUIRE( block["transactions"].size() == 1 ); + BOOST_REQUIRE( block["transactions"][0]["hash"].asString() == txHash ); + BOOST_REQUIRE( block["transactions"][0]["type"] == "0x2" ); + BOOST_REQUIRE( toJS( jsToInt( block["transactions"][0]["yParity"].asString() ) + 35 + 2 * fixture.client->chainParams().chainID ) == block["transactions"][0]["v"].asString() ); + BOOST_REQUIRE( block["transactions"][0]["accessList"].isArray() ); + + std::string blockHash = block["hash"].asString(); + + receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE( receipt["status"] == string( "0x1" ) ); + BOOST_REQUIRE( receipt["type"] == "0x2" ); + BOOST_REQUIRE( receipt["effectiveGasPrice"] == "0x4a817c800" ); + + result = fixture.rpcClient->eth_getTransactionByHash( txHash ); + BOOST_REQUIRE( result["hash"].asString() == txHash ); + BOOST_REQUIRE( result["type"] == "0x2" ); + BOOST_REQUIRE( toJS( jsToInt( result["yParity"].asString() ) + 35 + 2 * fixture.client->chainParams().chainID ) == result["v"].asString() ); + BOOST_REQUIRE( result["accessList"].isArray() ); + BOOST_REQUIRE( result.isMember( "maxPriorityFeePerGas" ) && result["maxPriorityFeePerGas"].isString() ); + BOOST_REQUIRE( result.isMember( "maxFeePerGas" ) && result["maxFeePerGas"].isString() ); + + result = fixture.rpcClient->eth_getTransactionByBlockHashAndIndex( blockHash, "0x0" ); + BOOST_REQUIRE( result["hash"].asString() == txHash ); + BOOST_REQUIRE( result["type"] == "0x2" ); + BOOST_REQUIRE( toJS( jsToInt( result["yParity"].asString() ) + 35 + 2 * fixture.client->chainParams().chainID ) == result["v"].asString() ); + BOOST_REQUIRE( result["accessList"].isArray() ); + BOOST_REQUIRE( result["maxPriorityFeePerGas"] == "0x4a817c800" ); + BOOST_REQUIRE( result["maxFeePerGas"] == "0x4a817c800" ); + + result = fixture.rpcClient->eth_getTransactionByBlockNumberAndIndex( "0x4", "0x0" ); + BOOST_REQUIRE( result["hash"].asString() == txHash ); + BOOST_REQUIRE( result["type"] == "0x2" ); + BOOST_REQUIRE( toJS( jsToInt( result["yParity"].asString() ) + 35 + 2 * fixture.client->chainParams().chainID ) == result["v"].asString() ); + BOOST_REQUIRE( result["accessList"].isArray() ); + BOOST_REQUIRE( result["maxPriorityFeePerGas"] == "0x4a817c800" ); + BOOST_REQUIRE( result["maxFeePerGas"] == "0x4a817c800" ); +} + +BOOST_AUTO_TEST_CASE( eip2930RpcMethods ) { + std::string _config = c_genesisConfigString; + Json::Value ret; + Json::Reader().parse( _config, ret ); + + // Set chainID = 151 + ret["params"]["chainID"] = "0x97"; + + Json::FastWriter fastWriter; + std::string config = fastWriter.write( ret ); + JsonRpcFixture fixture( config ); + + dev::eth::simulateMining( *( fixture.client ), 20 ); + string senderAddress = toJS(fixture.coinbase.address()); + + Json::Value txRefill; + txRefill["to"] = "0x5EdF1e852fdD1B0Bc47C0307EF755C76f4B9c251"; + txRefill["from"] = senderAddress; + txRefill["gas"] = "100000"; + txRefill["gasPrice"] = fixture.rpcClient->eth_gasPrice(); + txRefill["value"] = 1000000; + string txHash = fixture.rpcClient->eth_sendTransaction( txRefill ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + + Json::Value receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE( receipt["status"] == string( "0x1" ) ); + BOOST_REQUIRE( receipt["type"] == "0x0" ); + + auto accessList = fixture.rpcClient->eth_createAccessList( txRefill, "latest" ); + BOOST_REQUIRE( accessList.isMember( "accessList" ) && accessList.isMember( "gasUsed" ) ); + BOOST_REQUIRE( accessList["accessList"].isArray() && accessList["accessList"].size() == 0 ); + BOOST_REQUIRE( accessList["gasUsed"].isString() ); +} + +BOOST_AUTO_TEST_CASE( eip1559RpcMethods ) { + std::string _config = c_genesisConfigString; + Json::Value ret; + Json::Reader().parse( _config, ret ); + + // Set chainID = 151 + ret["params"]["chainID"] = "0x97"; + time_t eip1559PatchActivationTimestamp = time(nullptr) + 5; + ret["skaleConfig"]["sChain"]["EIP1559TransactionsPatchTimestamp"] = eip1559PatchActivationTimestamp; + + Json::FastWriter fastWriter; + std::string config = fastWriter.write( ret ); + JsonRpcFixture fixture( config ); + + dev::eth::simulateMining( *( fixture.client ), 20 ); + string senderAddress = toJS(fixture.coinbase.address()); + + Json::Value txRefill; + txRefill["to"] = "0x5EdF1e852fdD1B0Bc47C0307EF755C76f4B9c251"; + txRefill["from"] = senderAddress; + txRefill["gas"] = "100000"; + txRefill["gasPrice"] = fixture.rpcClient->eth_gasPrice(); + txRefill["value"] = 100000000000000000; + for (size_t i = 0; i < 10; ++i) { + // mine 10 blocks + string txHash = fixture.rpcClient->eth_sendTransaction( txRefill ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + } + + BOOST_REQUIRE( fixture.rpcClient->eth_maxPriorityFeePerGas() == "0x0" ); + + auto bn = fixture.client->number(); + + Json::Value percentiles = Json::Value( Json::arrayValue ); + percentiles.resize( 2 ); + percentiles[0] = 20; + percentiles[1] = 80; + + size_t blockCnt = 9; + auto feeHistory = fixture.rpcClient->eth_feeHistory( toJS( blockCnt ), "latest", percentiles ); + + BOOST_REQUIRE( feeHistory["oldestBlock"] == toJS( bn - blockCnt + 1 ) ); + + BOOST_REQUIRE( feeHistory.isMember( "baseFeePerGas" ) ); + BOOST_REQUIRE( feeHistory["baseFeePerGas"].isArray() ); + + for (Json::Value::ArrayIndex i = 0; i < blockCnt; ++i) { + BOOST_REQUIRE( feeHistory["baseFeePerGas"][i].isString() ); + std::string estimatedBaseFeePerGas = EIP1559TransactionsPatch::isEnabledWhen( + fixture.client->blockInfo( bn - i - 1 ).timestamp() ) ? toJS( fixture.client->gasBidPrice( bn - i - 1 ) ) : toJS( 0 ); + BOOST_REQUIRE( feeHistory["baseFeePerGas"][i].asString() == estimatedBaseFeePerGas ); + BOOST_REQUIRE_GT( feeHistory["gasUsedRatio"][i].asDouble(), 0 ); + BOOST_REQUIRE_GT( 1, feeHistory["gasUsedRatio"][i].asDouble() ); + for ( Json::Value::ArrayIndex j = 0; j < percentiles.size(); ++j ) { + BOOST_REQUIRE_EQUAL( feeHistory["reward"][i][j].asString(), toJS( 0 ) ); + } + } +} + BOOST_AUTO_TEST_CASE( etherbase_generation2 ) { JsonRpcFixture fixture(c_genesisGeneration2ConfigString, false, false, true); string etherbase = fixture.rpcClient->eth_coinbase(); @@ -2744,6 +3358,121 @@ BOOST_AUTO_TEST_CASE( deploy_controller_generation2 ) { BOOST_REQUIRE( code.asString().substr( 2 ) == compiled.substr( 58 ) ); } +BOOST_AUTO_TEST_CASE( deployment_control_v2 ) { + + // Inserting ConfigController mockup into config and enabling flexibleDeploymentPatch. + // ConfigController mockup contract: + + // pragma solidity ^0.8.9; + // contract ConfigController { + // bool public freeContractDeployment = false; + // function isAddressWhitelisted(address addr) external view returns (bool) { + // return false; + // } + // function isDeploymentAllowed(address origin, address sender) + // external view returns (bool) { + // return freeContractDeployment; + // } + // function setFreeContractDeployment() external { + // freeContractDeployment = true; + // } + // } + + string configControllerV2 = + "0x608060405234801561001057600080fd5b506004361061004c576000" + "3560e01c806313f44d1014610051578063a2306c4f14610081578063d0" + "f557f41461009f578063f7e2a91b146100cf575b600080fd5b61006b60" + "048036038101906100669190610189565b6100d9565b60405161007891" + "906101d1565b60405180910390f35b6100896100e0565b604051610096" + "91906101d1565b60405180910390f35b6100b960048036038101906100" + "b491906101ec565b6100f1565b6040516100c691906101d1565b604051" + "80910390f35b6100d761010a565b005b6000919050565b600080549061" + "01000a900460ff1681565b60008060009054906101000a900460ff1690" + "5092915050565b60016000806101000a81548160ff0219169083151502" + "17905550565b600080fd5b600073ffffffffffffffffffffffffffffff" + "ffffffffff82169050919050565b60006101568261012b565b90509190" + "50565b6101668161014b565b811461017157600080fd5b50565b600081" + "3590506101838161015d565b92915050565b6000602082840312156101" + "9f5761019e610126565b5b60006101ad84828501610174565b91505092" + "915050565b60008115159050919050565b6101cb816101b6565b825250" + "50565b60006020820190506101e660008301846101c2565b9291505056" + "5b6000806040838503121561020357610202610126565b5b6000610211" + "85828601610174565b925050602061022285828601610174565b915050" + "925092905056fea2646970667358221220b5f971b16f7bbba22272b220" + "7e02f10abf1682c17fe636c7bf6406c5cae5716064736f6c63430008090033"; + + std::string _config = c_genesisGeneration2ConfigString; + Json::Value ret; + Json::Reader().parse( _config, ret ); + ret["accounts"]["0xD2002000000000000000000000000000000000d2"]["code"] = configControllerV2; + ret["skaleConfig"]["sChain"]["flexibleDeploymentPatchTimestamp"] = 1; + Json::FastWriter fastWriter; + std::string config = fastWriter.write( ret ); + + JsonRpcFixture fixture(config, false, false, true ); + Address senderAddress = fixture.coinbase.address(); + fixture.client->setAuthor( senderAddress ); + + // contract test { + // function f(uint a) returns(uint d) { return a * 7; } + // } + + string compiled = + "6080604052341561000f57600080fd5b60b98061001d6000396000f300" + "608060405260043610603f576000357c01000000000000000000000000" + "00000000000000000000000000000000900463ffffffff168063b3de64" + "8b146044575b600080fd5b3415604e57600080fd5b606a600480360381" + "019080803590602001909291905050506080565b604051808281526020" + "0191505060405180910390f35b60006007820290509190505600a16562" + "7a7a72305820f294e834212334e2978c6dd090355312a3f0f9476b8eb9" + "8fb480406fc2728a960029"; + + + // Trying to deploy contract without permission + Json::Value deployContractWithoutRoleTx; + deployContractWithoutRoleTx["from"] = senderAddress.hex(); + deployContractWithoutRoleTx["code"] = compiled; + deployContractWithoutRoleTx["gas"] = "1000000"; + deployContractWithoutRoleTx["gasPrice"] = fixture.rpcClient->eth_gasPrice(); + + string txHash = fixture.rpcClient->eth_sendTransaction( deployContractWithoutRoleTx ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + + Json::Value receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE_EQUAL( receipt["status"], string( "0x0" ) ); + + Json::Value code = + fixture.rpcClient->eth_getCode( receipt["contractAddress"].asString(), "latest" ); + BOOST_REQUIRE( code.asString() == "0x" ); + + // Allow to deploy by calling setFreeContractDeployment() + Json::Value grantDeployerRoleTx; + grantDeployerRoleTx["data"] = "0xf7e2a91b"; + grantDeployerRoleTx["from"] = senderAddress.hex(); + grantDeployerRoleTx["to"] = "0xD2002000000000000000000000000000000000D2"; + grantDeployerRoleTx["gasPrice"] = fixture.rpcClient->eth_gasPrice(); + grantDeployerRoleTx["gas"] = toJS( "1000000" ); + txHash = fixture.rpcClient->eth_sendTransaction( grantDeployerRoleTx ); + BOOST_REQUIRE( !txHash.empty() ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + + // Deploying with permission + Json::Value deployContractTx; + deployContractTx["from"] = senderAddress.hex(); + deployContractTx["code"] = compiled; + deployContractTx["gas"] = "1000000"; + deployContractTx["gasPrice"] = fixture.rpcClient->eth_gasPrice(); + + txHash = fixture.rpcClient->eth_sendTransaction( deployContractTx ); + dev::eth::mineTransaction( *( fixture.client ), 1 ); + + receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); + BOOST_REQUIRE_EQUAL( receipt["status"], string( "0x1" ) ); + BOOST_REQUIRE( !receipt["contractAddress"].isNull() ); + code = fixture.rpcClient->eth_getCode( receipt["contractAddress"].asString(), "latest" ); + BOOST_REQUIRE( code.asString().substr( 2 ) == compiled.substr( 58 ) ); +} + BOOST_AUTO_TEST_CASE( filestorage_generation2 ) { JsonRpcFixture fixture(c_genesisGeneration2ConfigString, false, false, true); @@ -2868,9 +3597,7 @@ BOOST_AUTO_TEST_CASE( PrecompiledPrintFakeEth, *boost::unit_test::precondition( pair< bool, Secret > ar = fixture.accountHolder->authenticate( ts ); Transaction tx( ts, ar.second ); - RLPStream stream; - tx.streamRLP( stream ); - auto txHash = fixture.rpcClient->eth_sendRawTransaction( toJS( stream.out() ) ); + auto txHash = fixture.rpcClient->eth_sendRawTransaction( toJS( tx.toBytes() ) ); dev::eth::mineTransaction( *( fixture.client ), 1 ); Json::Value receipt = fixture.rpcClient->eth_getTransactionReceipt( txHash ); @@ -3247,9 +3974,7 @@ BOOST_AUTO_TEST_CASE( transaction_from_restricted_address ) { pair< bool, Secret > ar = accountHolder->authenticate( ts ); Transaction tx( ts, ar.second ); - RLPStream stream; - tx.streamRLP( stream ); - auto txHash = rpcClient->eth_sendRawTransaction( toJS( stream.out() ) ); + auto txHash = rpcClient->eth_sendRawTransaction( toJS( tx.toBytes() ) ); dev::eth::mineTransaction( *( client ), 1 ); BOOST_REQUIRE( !boost::filesystem::exists( path ) ); @@ -3270,9 +3995,7 @@ BOOST_AUTO_TEST_CASE( transaction_from_allowed_address ) { pair< bool, Secret > ar = accountHolder->authenticate( ts ); Transaction tx( ts, ar.second ); - RLPStream stream; - tx.streamRLP( stream ); - auto txHash = rpcClient->eth_sendRawTransaction( toJS( stream.out() ) ); + auto txHash = rpcClient->eth_sendRawTransaction( toJS( tx.toBytes() ) ); dev::eth::mineTransaction( *( client ), 1 ); BOOST_REQUIRE( boost::filesystem::exists( path ) ); @@ -3321,9 +4044,7 @@ BOOST_AUTO_TEST_CASE( delegate_call ) { pair< bool, Secret > ar = accountHolder->authenticate( ts ); Transaction tx( ts, ar.second ); - RLPStream stream; - tx.streamRLP( stream ); - auto txHash = rpcClient->eth_sendRawTransaction( toJS( stream.out() ) ); + auto txHash = rpcClient->eth_sendRawTransaction( toJS( tx.toBytes() ) ); dev::eth::mineTransaction( *( client ), 1 ); Json::Value receipt = rpcClient->eth_getTransactionReceipt( txHash ); @@ -3354,7 +4075,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 ); @@ -3373,9 +4094,7 @@ BOOST_AUTO_TEST_CASE( cached_filestorage ) { pair< bool, Secret > ar = fixture.accountHolder->authenticate( ts ); Transaction tx( ts, ar.second ); - RLPStream stream; - tx.streamRLP( stream ); - auto txHash = fixture.rpcClient->eth_sendRawTransaction( toJS( stream.out() ) ); + auto txHash = fixture.rpcClient->eth_sendRawTransaction( toJS( tx.toBytes() ) ); dev::eth::mineTransaction( *( fixture.client ), 1 ); BOOST_REQUIRE( !boost::filesystem::exists( fixture.path ) ); @@ -3405,9 +4124,7 @@ BOOST_AUTO_TEST_CASE( uncached_filestorage ) { pair< bool, Secret > ar = fixture.accountHolder->authenticate( ts ); Transaction tx( ts, ar.second ); - RLPStream stream; - tx.streamRLP( stream ); - auto txHash = fixture.rpcClient->eth_sendRawTransaction( toJS( stream.out() ) ); + auto txHash = fixture.rpcClient->eth_sendRawTransaction( toJS( tx.toBytes() ) ); dev::eth::mineTransaction( *( fixture.client ), 1 ); BOOST_REQUIRE( boost::filesystem::exists( fixture.path ) ); diff --git a/test/unittests/mapreduce_consensus/ConsensusEngine.cpp b/test/unittests/mapreduce_consensus/ConsensusEngine.cpp index 8b78e0bd1..8c6932c1d 100644 --- a/test/unittests/mapreduce_consensus/ConsensusEngine.cpp +++ b/test/unittests/mapreduce_consensus/ConsensusEngine.cpp @@ -305,7 +305,7 @@ class ConsensusExtFaceFixture : public ConsensusExtFace { assert( buffer.empty() ); for ( const Transaction& txn : txns ) { - buffer.push_back( txn.rlp() ); + buffer.push_back( txn.toBytes() ); } // for m_transactionsCond.notify_one();