diff --git a/.github/workflows/functional-tests.yml b/.github/workflows/functional-tests.yml index 489c82421..14d743434 100644 --- a/.github/workflows/functional-tests.yml +++ b/.github/workflows/functional-tests.yml @@ -65,8 +65,8 @@ - name: skaled+load_python+all run: SKALED_PROVIDER=skaled_providers/binary_from_container ./run_tests.sh skaled+load_python+all - # - name: skaled+load_js+run_angry_cats - # run: SKALED_PROVIDER=skaled_providers/endpoint_by_container ./run_tests.sh skaled+load_js+run_angry_cats + - name: skaled+load_js+run_angry_cats + run: SKALED_PROVIDER=skaled_providers/endpoint_by_container ./run_tests.sh skaled+load_js+run_angry_cats - name: skaled+internals+test_snapshot_api run: SKALED_PROVIDER=skaled_providers/binary_from_container ./run_tests.sh skaled+internals+test_snapshot_api diff --git a/VERSION b/VERSION index 3f67e25ce..c5b45eb7b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.17.0 +3.18.0 diff --git a/libconsensus b/libconsensus index 5a9b85d8f..79d6b7fd6 160000 --- a/libconsensus +++ b/libconsensus @@ -1 +1 @@ -Subproject commit 5a9b85d8f171e1ba5f7dfe244cff70e0e39aa5f4 +Subproject commit 79d6b7fd676e5f52aed5ff626066e810d5219f13 diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt index 19f5f4b85..99d179d1e 100644 --- a/libdevcore/CMakeLists.txt +++ b/libdevcore/CMakeLists.txt @@ -15,8 +15,9 @@ add_library(devcore ${sources} ${headers}) add_dependencies(devcore secp256k1) target_compile_options( devcore PRIVATE - -Wno-error=deprecated-copy -Wno-error=unused-result -Wno-error=unused-parameter -Wno-error=unused-variable -Wno-error=maybe-uninitialized - ) + -Wno-error=deprecated-copy -Wno-error=unused-result -Wno-error=unused-parameter + -Wno-error=unused-variable -Wno-error=maybe-uninitialized -Wno-error=class-memaccess +) # Needed to prevent including system-level boost headers: target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIR} PRIVATE ../utils) diff --git a/libethcore/ChainOperationParams.h b/libethcore/ChainOperationParams.h index ec57fe4ed..db6e52683 100644 --- a/libethcore/ChainOperationParams.h +++ b/libethcore/ChainOperationParams.h @@ -175,6 +175,7 @@ struct SChain { time_t verifyDaSigsPatchTimestamp = 0; time_t storageDestructionPatchTimestamp = 0; time_t powCheckPatchTimestamp = 0; + time_t pushZeroPatchTimestamp = 0; time_t skipInvalidTransactionsPatchTimestamp = 0; SChain() { diff --git a/libethereum/ChainParams.cpp b/libethereum/ChainParams.cpp index fb48f748d..8ed23de33 100644 --- a/libethereum/ChainParams.cpp +++ b/libethereum/ChainParams.cpp @@ -266,6 +266,10 @@ ChainParams ChainParams::loadConfig( sChainObj.at( "powCheckPatchTimestamp" ).get_int64() : 0; + s.pushZeroPatchTimestamp = sChainObj.count( "pushZeroPatchTimestamp" ) ? + sChainObj.at( "pushZeroPatchTimestamp" ).get_int64() : + 0; + s.skipInvalidTransactionsPatchTimestamp = sChainObj.count( "skipInvalidTransactionsPatchTimestamp" ) ? sChainObj.at( "skipInvalidTransactionsPatchTimestamp" ).get_int64() : diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 06cec9516..d20bdc950 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -163,6 +164,7 @@ Client::Client( ChainParams const& _params, int _networkID, 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 ); } @@ -657,6 +659,7 @@ size_t Client::syncTransactions( 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(); DEV_WRITE_GUARDED( x_working ) { diff --git a/libethereum/ValidationSchemes.cpp b/libethereum/ValidationSchemes.cpp index 38231c130..fdf83fdeb 100644 --- a/libethereum/ValidationSchemes.cpp +++ b/libethereum/ValidationSchemes.cpp @@ -268,6 +268,8 @@ void validateConfigJson( js::mObject const& _obj ) { { "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 } } } ); diff --git a/libevm/Instruction.cpp b/libevm/Instruction.cpp index 78e8eec2d..bb4f25d20 100644 --- a/libevm/Instruction.cpp +++ b/libevm/Instruction.cpp @@ -86,6 +86,10 @@ static const std::map c_instructionInfo = { Instruction::MSIZE, { "MSIZE", 0, 1, Tier::Base } }, { Instruction::GAS, { "GAS", 0, 1, Tier::Base } }, { Instruction::JUMPDEST, { "JUMPDEST", 0, 0, Tier::Special } }, + // As per EIP-3855 PUSH0 instruction tire is base (2 gas units) + // As all other PUSH instructions, it removes zero elements from stack and + // pushes 1 element to stack + { Instruction::PUSH0, { "PUSH0", 0, 1, Tier::Base } }, { Instruction::PUSH1, { "PUSH1", 0, 1, Tier::VeryLow } }, { Instruction::PUSH2, { "PUSH2", 0, 1, Tier::VeryLow } }, { Instruction::PUSH3, { "PUSH3", 0, 1, Tier::VeryLow } }, diff --git a/libevm/Instruction.h b/libevm/Instruction.h index 573b57107..0dc52bdad 100644 --- a/libevm/Instruction.h +++ b/libevm/Instruction.h @@ -94,6 +94,7 @@ enum class Instruction : uint8_t { GAS, ///< get the amount of available gas JUMPDEST, ///< set a potential jump destination + PUSH0 = 0x5f, // EIP-3855 PUSH1 = 0x60, ///< place 1 byte item on stack PUSH2, ///< place 2 byte item on stack PUSH3, ///< place 3 byte item on stack diff --git a/libevm/LegacyVM.cpp b/libevm/LegacyVM.cpp index 8a0775112..a987a3b0c 100644 --- a/libevm/LegacyVM.cpp +++ b/libevm/LegacyVM.cpp @@ -16,6 +16,7 @@ */ #include "LegacyVM.h" +#include "libskale/PushZeroPatch.h" using namespace std; using namespace dev; @@ -1355,6 +1356,21 @@ void LegacyVM::interpretCases() { } CONTINUE + // EIP-3855. Code PUSH0 is similar to PUSH1 but pushes 0 + // 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() ) { + throwBadInstruction(); + } + ON_OP(); + updateIOGas(); + m_SPP[0] = 0; + ++m_PC; + }; + CONTINUE + + CASE( PUSH1 ) { ON_OP(); updateIOGas(); diff --git a/libevm/LegacyVMConfig.h b/libevm/LegacyVMConfig.h index 4b8bde611..9908d712c 100644 --- a/libevm/LegacyVMConfig.h +++ b/libevm/LegacyVMConfig.h @@ -254,7 +254,7 @@ namespace eth { &&BEGINDATA, \ &&BEGINSUB, \ &&INVALID, \ - &&INVALID, \ + &&PUSH0, /* EIP-3855 */ \ &&PUSH1, /* 60, */ \ &&PUSH2, \ &&PUSH3, \ diff --git a/libskale/CMakeLists.txt b/libskale/CMakeLists.txt index 73acfbbf5..ba774b211 100644 --- a/libskale/CMakeLists.txt +++ b/libskale/CMakeLists.txt @@ -20,6 +20,7 @@ set(sources OverlayFS.cpp StorageDestructionPatch.cpp POWCheckPatch.cpp + PushZeroPatch.cpp SkipInvalidTransactionsPatch.cpp ) diff --git a/libskale/PushZeroPatch.cpp b/libskale/PushZeroPatch.cpp new file mode 100644 index 000000000..e7ef4f6c1 --- /dev/null +++ b/libskale/PushZeroPatch.cpp @@ -0,0 +1,11 @@ +#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 new file mode 100644 index 000000000..6ebab4e9f --- /dev/null +++ b/libskale/PushZeroPatch.h @@ -0,0 +1,27 @@ +#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/libskutils/src/http_pg.cpp b/libskutils/src/http_pg.cpp index fc2fb766d..83952d64c 100644 --- a/libskutils/src/http_pg.cpp +++ b/libskutils/src/http_pg.cpp @@ -203,6 +203,7 @@ void request_site::onEOM() noexcept { } else { std::string strOut = rslt.joOut_.dump(); bldr.header( "content-length", skutils::tools::format( "%zu", strOut.size() ) ); + bldr.header( "Content-Type", "application/json" ); bldr.body( strOut ); } bldr.sendWithEOM(); diff --git a/libweb3jsonrpc/Eth.cpp b/libweb3jsonrpc/Eth.cpp index b2330bc51..131697e5d 100644 --- a/libweb3jsonrpc/Eth.cpp +++ b/libweb3jsonrpc/Eth.cpp @@ -174,7 +174,10 @@ Json::Value Eth::eth_accounts() { return toJson( m_ethAccounts.allAccounts() ); } -string Eth::eth_blockNumber() { +string Eth::eth_blockNumber( const Json::Value& request ) { + if ( !request.empty() ) { + BOOST_THROW_EXCEPTION( JsonRpcException( Errors::ERROR_RPC_INVALID_PARAMS ) ); + } return toJS( client()->number() ); } diff --git a/libweb3jsonrpc/Eth.h b/libweb3jsonrpc/Eth.h index e9504732f..694461c7e 100644 --- a/libweb3jsonrpc/Eth.h +++ b/libweb3jsonrpc/Eth.h @@ -145,7 +145,7 @@ class Eth : public dev::rpc::EthFace, public skutils::json_config_file_accessor virtual bool eth_mining() override; virtual std::string eth_gasPrice() override; virtual Json::Value eth_accounts() override; - virtual std::string eth_blockNumber() override; + virtual std::string eth_blockNumber( const Json::Value& request ) override; virtual std::string eth_getBalance( std::string const& _address, std::string const& _blockNumber ) override; virtual std::string eth_getStorageAt( std::string const& _address, std::string const& _position, diff --git a/libweb3jsonrpc/EthFace.h b/libweb3jsonrpc/EthFace.h index 7c31b4f62..648d4424c 100644 --- a/libweb3jsonrpc/EthFace.h +++ b/libweb3jsonrpc/EthFace.h @@ -244,8 +244,7 @@ class EthFace : public ServerInterface< EthFace > { response = this->eth_accounts(); } inline virtual void eth_blockNumberI( const Json::Value& request, Json::Value& response ) { - ( void ) request; - response = this->eth_blockNumber(); + response = this->eth_blockNumber( request ); } inline virtual void eth_getBalanceI( const Json::Value& request, Json::Value& response ) { response = this->eth_getBalance( request[0u].asString(), request[1u].asString() ); @@ -432,7 +431,7 @@ class EthFace : public ServerInterface< EthFace > { virtual bool eth_mining() = 0; virtual std::string eth_gasPrice() = 0; virtual Json::Value eth_accounts() = 0; - virtual std::string eth_blockNumber() = 0; + virtual std::string eth_blockNumber( const Json::Value& request ) = 0; virtual std::string eth_getBalance( const std::string& param1, const std::string& param2 ) = 0; virtual std::string eth_getStorageAt( const std::string& param1, const std::string& param2, const std::string& param3 ) = 0; diff --git a/scripts/build_and_publish.sh b/scripts/build_and_publish.sh index 366c6bc7b..f02f0b0c0 100644 --- a/scripts/build_and_publish.sh +++ b/scripts/build_and_publish.sh @@ -9,14 +9,17 @@ NAME=schain REPO_NAME=skalenetwork/$NAME IMAGE_NAME=$REPO_NAME:$VERSION -LABEL="develop" -if [ $BRANCH = "stable" ] -then - LABEL="stable" -elif [ $BRANCH = "beta" ] +# 3.17.0-develop.22 -> 3.17.0-develop +# 3.17.0-develop.22-hostoric -> 3.17.0-develop +LABEL="${VERSION%.*}" + +# 3.17.0 -> 3.17.0 +# 3.17.0-historic -> 3.17.0 +if [[ "$BRANCH" == "stable" ]] then - LABEL="beta" + LABEL=${VERSION%-historic} fi + LATEST_IMAGE_NAME=$REPO_NAME:$LABEL-latest if [[ $VERSION == *"historic" ]] @@ -44,8 +47,4 @@ echo "Built $IMAGE_NAME" echo "$DOCKER_PASSWORD" | docker login --username $DOCKER_USERNAME --password-stdin docker push $IMAGE_NAME || exit $? - -if [ $BRANCH = $LABEL ] -then - docker push $LATEST_IMAGE_NAME || exit $? -fi +docker push $LATEST_IMAGE_NAME || exit $? diff --git a/skaled/main.cpp b/skaled/main.cpp index 8118b017e..d33635a2f 100644 --- a/skaled/main.cpp +++ b/skaled/main.cpp @@ -550,6 +550,7 @@ int main( int argc, char** argv ) try { cc::_on_ = false; cc::_max_value_size_ = 2048; MicroProfileSetEnableAllGroups( true ); + dev::setThreadName( "main" ); BlockHeader::useTimestampHack = false; srand( time( nullptr ) ); setCLocale(); @@ -2773,7 +2774,6 @@ int main( int argc, char** argv ) try { << cc::debug( "Done, programmatic shutdown via Web3 is disabled" ); } - dev::setThreadName( "main" ); if ( g_client ) { unsigned int n = g_client->blockChain().details().number; unsigned int mining = 0; diff --git a/test/historicstate/configs/basic_config.json b/test/historicstate/configs/basic_config.json index aa5915277..ea3bbf1d6 100644 --- a/test/historicstate/configs/basic_config.json +++ b/test/historicstate/configs/basic_config.json @@ -322,10 +322,11 @@ "sChain": { "schainName": "TestChain", - "schainID": 1, + "schainID": 5, "schainOwner": "0x907cd0881E50d359bb9Fd120B1A5A143b1C97De6", "contractStorageLimit": 10000000000, "emptyBlockIntervalMs": 10000, + "pushZeroPatchTimestamp": 1, "nodes": [ { "nodeID": 1112, "ip": "127.0.0.1", "basePort": 1231, "schainIndex" : 1, "publicKey":""} ] diff --git a/test/historicstate/hardhat/contracts/Push0.sol b/test/historicstate/hardhat/contracts/Push0.sol new file mode 100644 index 000000000..e09e457b6 --- /dev/null +++ b/test/historicstate/hardhat/contracts/Push0.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract Push0 { + + uint256 public constant ZERO = 0; + + function getZero() public { + // this triggers compiler using push0 to stack since operations use lots of zeros + uint256 one = 0; + one = one + 1 + ZERO; + uint256 two = one * 0; + uint256 three = one * ZERO; + } +} \ No newline at end of file diff --git a/test/historicstate/hardhat/hardhat.config.js b/test/historicstate/hardhat/hardhat.config.js index d369e02ba..3ff48bfba 100644 --- a/test/historicstate/hardhat/hardhat.config.js +++ b/test/historicstate/hardhat/hardhat.config.js @@ -10,7 +10,7 @@ module.exports = { const INSECURE_PRIVATE_KEY = "bd200f4e7f597f3c2c77fb405ee7fabeb249f63f03f43d5927b4fa0c43cfe85e"; module.exports = { - solidity: "0.8.9", + solidity: "0.8.20", networks: { skaled: { url: `http://localhost:1234`, diff --git a/test/historicstate/hardhat/scripts/push0_test.ts b/test/historicstate/hardhat/scripts/push0_test.ts new file mode 100644 index 000000000..d76a0a934 --- /dev/null +++ b/test/historicstate/hardhat/scripts/push0_test.ts @@ -0,0 +1,71 @@ +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`); + + const transferReceipt = await testContract.getZero() + console.log(`Gas limit ${transferReceipt.gasLimit}`); + + +} + +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