diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 233ec9f3faf..64005606fba 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -186,6 +186,9 @@ class Executive /// Revert all changes made to the state by this execution. void revert(); + /// Used only in tests + ExtVM const& extVM() const { return *m_ext; } + private: /// @returns false iff go() must be called (and thus a VM execution in required). bool createWithAddressFromNonceAndSender(Address const& _sender, u256 const& _endowment, diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2fdeaae7002..a27382b00ea 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -21,6 +21,7 @@ set(unittest_sources unittests/libethcore/CommonJS.cpp unittests/libethcore/KeyManager.cpp + unittests/libethereum/ExecutiveTest.cpp unittests/libethereum/ValidationSchemes.cpp unittests/libp2p/capability.cpp diff --git a/test/unittests/libethereum/ExecutiveTest.cpp b/test/unittests/libethereum/ExecutiveTest.cpp new file mode 100644 index 00000000000..9444fe81f0a --- /dev/null +++ b/test/unittests/libethereum/ExecutiveTest.cpp @@ -0,0 +1,148 @@ +// Aleth: Ethereum C++ client, tools and libraries. +// Copyright 2019 Aleth Authors. +// Licensed under the GNU General Public License, Version 3. + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace dev; +using namespace dev::eth; +using namespace dev::test; + +class ExecutiveTest : public testing::Test +{ +public: + ExecutiveTest() + { + ethash.setChainParams(ChainParams{genesisInfo(eth::Network::IstanbulTransitionTest)}); + } + + Ethash ethash; + BlockHeader blockHeader; + TestLastBlockHashes lastBlockHashes{{}}; + State state{0}; + + Address receiveAddress{"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"}; + Address txSender{"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}; + u256 txValue; + u256 gasPrice; + bytesConstRef txData{}; + u256 gas = 1000000; + bytes code = {1, 2, 3, 4}; +}; + +TEST_F(ExecutiveTest, callUsesAccountVersion) +{ + EnvInfo envInfo(blockHeader, lastBlockHashes, 0); + + state.createContract(receiveAddress); + u256 version = 1; + state.setCode(receiveAddress, bytes{code}, version); + state.commit(State::CommitBehaviour::RemoveEmptyAccounts); + + Executive executive(state, envInfo, ethash); + + bool done = executive.call(receiveAddress, txSender, txValue, gasPrice, txData, gas); + + EXPECT_FALSE(done); + EXPECT_EQ(executive.extVM().version, version); +} + +TEST_F(ExecutiveTest, createUsesLatestForkVersion) +{ + // block in Istanbul fork + blockHeader.setNumber(10); + + EnvInfo envInfo(blockHeader, lastBlockHashes, 0); + + Executive executive(state, envInfo, ethash); + + bool done = executive.create(txSender, txValue, gasPrice, gas, ref(code), txSender); + + EXPECT_FALSE(done); + EXPECT_EQ(executive.extVM().version, IstanbulSchedule.version); +} + +TEST_F(ExecutiveTest, createOpcodeUsesParentVersion) +{ + EnvInfo envInfo(blockHeader, lastBlockHashes, 0); + + state.createContract(txSender); + u256 version = 1; + state.setCode(txSender, bytes{code}, version); + state.commit(State::CommitBehaviour::RemoveEmptyAccounts); + + Executive executive(state, envInfo, ethash); + + bool done = executive.createOpcode(txSender, txValue, gasPrice, gas, ref(code), txSender); + + EXPECT_FALSE(done); + EXPECT_EQ(executive.extVM().version, version); +} + +TEST_F(ExecutiveTest, create2OpcodeUsesParentVersion) +{ + EnvInfo envInfo(blockHeader, lastBlockHashes, 0); + + state.createContract(txSender); + u256 version = 1; + state.setCode(txSender, bytes{code}, version); + state.commit(State::CommitBehaviour::RemoveEmptyAccounts); + + Executive executive(state, envInfo, ethash); + + bool done = executive.create2Opcode(txSender, txValue, gasPrice, gas, ref(code), txSender, 0); + + EXPECT_FALSE(done); + EXPECT_EQ(executive.extVM().version, version); +} + +TEST_F(ExecutiveTest, emptyInitCodeSetsParentVersion) +{ + EnvInfo envInfo(blockHeader, lastBlockHashes, 0); + + state.createContract(txSender); + u256 version = 1; + state.setCode(txSender, bytes{code}, version); + state.commit(State::CommitBehaviour::RemoveEmptyAccounts); + + Executive executive(state, envInfo, ethash); + + bytes initCode; + bool done = executive.createOpcode(txSender, txValue, gasPrice, gas, ref(initCode), txSender); + + EXPECT_TRUE(done); + EXPECT_FALSE(state.addressHasCode(executive.newAddress())); + EXPECT_EQ(state.version(executive.newAddress()), version); +} + +TEST_F(ExecutiveTest, createdContractHasParentVersion) +{ + EnvInfo envInfo(blockHeader, lastBlockHashes, 0); + + state.createContract(txSender); + u256 version = 1; + state.setCode(txSender, bytes{code}, version); + state.commit(State::CommitBehaviour::RemoveEmptyAccounts); + + Executive executive(state, envInfo, ethash); + + // mstore(0, 0x60) + // return(0, 0x20) + bytes initCode = fromHex("606060005260206000f3"); + + bool done = executive.createOpcode(txSender, txValue, gasPrice, gas, ref(initCode), txSender); + EXPECT_FALSE(done); + + done = executive.go(); + EXPECT_TRUE(done); + + EXPECT_TRUE(state.addressHasCode(executive.newAddress())); + EXPECT_EQ(state.version(executive.newAddress()), version); +} diff --git a/test/unittests/libethereum/StateUnitTests.cpp b/test/unittests/libethereum/StateUnitTests.cpp index e39524702aa..ec804c43606 100644 --- a/test/unittests/libethereum/StateUnitTests.cpp +++ b/test/unittests/libethereum/StateUnitTests.cpp @@ -66,8 +66,11 @@ BOOST_AUTO_TEST_CASE(RollbackSetCode) s.setCode(addr, {std::begin(codeData), std::end(codeData)}, version); s.rollback(savepoint); - BOOST_CHECK(!s.addressHasCode(addr)); - BOOST_CHECK(!s.accountNonemptyAndExisting(addr)); + BOOST_CHECK(!s.addressInUse(addr)); + BOOST_CHECK(s.version(addr) == 0); + + // only state root exists in DB + BOOST_CHECK_EQUAL(s.db().keys().size(), 1); } BOOST_AUTO_TEST_CASE(CodeVersionZero) @@ -85,6 +88,23 @@ BOOST_AUTO_TEST_CASE(CodeVersionZero) BOOST_CHECK_EQUAL(s.version(addr), version); } +BOOST_AUTO_TEST_CASE(SetEmptyCodeNonZeroVersion) +{ + Address addr{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}; + State s{0}; + s.createContract(addr); + s.setNonce(addr, 1); + u256 version = 123; + s.setCode(addr, {}, version); + s.commit(State::CommitBehaviour::RemoveEmptyAccounts); + + BOOST_CHECK(!s.addressHasCode(addr)); + BOOST_CHECK_EQUAL(s.version(addr), version); + + // empty code is not saved to DB + BOOST_CHECK(!s.db().exists(EmptySHA3)); +} + class AddressRangeTestFixture : public TestOutputHelperFixture { public: