From 052b98cc904f8507b6a3a1c6a1e54b53c3c2fdf4 Mon Sep 17 00:00:00 2001 From: timemarkovqtum Date: Tue, 10 Sep 2024 12:16:09 +0200 Subject: [PATCH] Add transient storage unit tests --- src/Makefile.test.include | 3 +- src/test/qtumtests/cancunfork_tests.cpp | 147 ++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 src/test/qtumtests/cancunfork_tests.cpp diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 32f98ccbad..2b5bf71fa6 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -198,7 +198,8 @@ BITCOIN_TESTS =\ test/qtumtests/istanbulfork_tests.cpp \ test/qtumtests/londonfork_tests.cpp \ test/qtumtests/evmone_tests.cpp \ - test/qtumtests/shanghaifork_tests.cpp + test/qtumtests/shanghaifork_tests.cpp \ + test/qtumtests/cancunfork_tests.cpp if ENABLE_WALLET BITCOIN_TESTS += \ diff --git a/src/test/qtumtests/cancunfork_tests.cpp b/src/test/qtumtests/cancunfork_tests.cpp new file mode 100644 index 0000000000..1250bcee2b --- /dev/null +++ b/src/test/qtumtests/cancunfork_tests.cpp @@ -0,0 +1,147 @@ +#include +#include +#include +#include + +namespace CancunTest{ + +const dev::u256 GASLIMIT = dev::u256(500000); +const dev::h256 HASHTX = dev::h256(ParseHex("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + +// Codes used to check that cancun fork +const std::vector CODE = { + /* + pragma solidity ^0.8.24; + + contract MulService { + function setMultiplier(uint multiplier) external { + assembly { + tstore(0, multiplier) + } + } + + function getMultiplier() private view returns (uint multiplier) { + assembly { + multiplier := tload(0) + } + } + + function multiply(uint value) external view returns (uint) { + return value * getMultiplier(); + } + } + */ valtype(ParseHex("6080604052348015600e575f80fd5b506101db8061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c8063641579a614610038578063c6888fa114610054575b5f80fd5b610052600480360381019061004d91906100e4565b610084565b005b61006e600480360381019061006991906100e4565b61008a565b60405161007b919061011e565b60405180910390f35b805f5d50565b5f6100936100a5565b8261009e9190610164565b9050919050565b5f805c905090565b5f80fd5b5f819050919050565b6100c3816100b1565b81146100cd575f80fd5b50565b5f813590506100de816100ba565b92915050565b5f602082840312156100f9576100f86100ad565b5b5f610106848285016100d0565b91505092915050565b610118816100b1565b82525050565b5f6020820190506101315f83018461010f565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61016e826100b1565b9150610179836100b1565b9250828202610187816100b1565b9150828204841483151761019e5761019d610137565b5b509291505056fea26469706673582212203dda524f45e82e20c7a5140d36ef2410c23bec4dee31ca21bc368cf508cde0f764736f6c634300081a0033")), + // setMultiplier(5) + valtype(ParseHex("641579a60000000000000000000000000000000000000000000000000000000000000005")), + // multiply(7) + valtype(ParseHex("c6888fa10000000000000000000000000000000000000000000000000000000000000007")), +}; + +// Codes IDs used to check that london fork is present +enum class CodeID +{ + mulService = 0, + setMultiplier_5, + multiply_7, +}; + +// Get the code identified by the ID +valtype getCode(CodeID id) +{ + return CODE[(int)id]; +} + +void genesisLoading(){ + const CChainParams& chainparams = Params(); + int coinbaseMaturity = Params().GetConsensus().CoinbaseMaturity(0); + int forkHeight = coinbaseMaturity + 499; + dev::eth::EVMConsensus evmConsensus; + evmConsensus.QIP6Height = coinbaseMaturity; + evmConsensus.QIP7Height = coinbaseMaturity; + evmConsensus.nMuirGlacierHeight = coinbaseMaturity; + evmConsensus.nLondonHeight = coinbaseMaturity; + evmConsensus.nShanghaiHeight = coinbaseMaturity; + evmConsensus.nCancunHeight = forkHeight; + UpdateCancunHeight(forkHeight); + dev::eth::ChainParams cp(chainparams.EVMGenesisInfo(evmConsensus)); + globalState->populateFrom(cp.genesisState); + globalSealEngine = std::unique_ptr(cp.createSealEngine()); + globalState->db().commit(); +} + +void createNewBlocks(TestChain100Setup* testChain100Setup, size_t n){ + std::function generateBlocks = [&](size_t n){ + dev::h256 oldHashStateRoot = globalState->rootHash(); + dev::h256 oldHashUTXORoot = globalState->rootHashUTXO(); + for(size_t i = 0; i < n; i++){ + testChain100Setup->CreateAndProcessBlock({}, GetScriptForRawPubKey(testChain100Setup->coinbaseKey.GetPubKey())); + } + globalState->setRoot(oldHashStateRoot); + globalState->setRootUTXO(oldHashUTXORoot); + }; + + generateBlocks(n); +} +BOOST_FIXTURE_TEST_SUITE(cancunfork_tests, TestChain100Setup) + +BOOST_AUTO_TEST_CASE(checking_transient_storage_after_fork){ + genesisLoading(); + createNewBlocks(this, 499); + dev::h256 hashTx(HASHTX); + + // Create contract + std::vector txs; + txs.push_back(createQtumTransaction(getCode(CodeID::mulService), 0, GASLIMIT, dev::u256(1), ++hashTx, dev::Address())); + auto result = executeBC(txs, *m_node.chainman); + BOOST_CHECK(result.first[0].execRes.excepted == dev::eth::TransactionException::None); + + // Create a transaction with 2 outputs that use transient storage. + // The first output set the multiplier to 5. + // The second output multiply the multiplier by 7, and the result is 35. + dev::Address proxy = createQtumAddress(txs[0].getHashWith(), txs[0].getNVout()); + std::vector txCancun; + txCancun.push_back(createQtumTransaction(getCode(CodeID::setMultiplier_5), 0, GASLIMIT, dev::u256(1), ++hashTx, proxy)); + txCancun.push_back(createQtumTransaction(getCode(CodeID::multiply_7), 0, GASLIMIT, dev::u256(1), ++hashTx, proxy)); + result = executeBC(txCancun, *m_node.chainman); + BOOST_CHECK(result.first[0].execRes.excepted == dev::eth::TransactionException::None); + BOOST_CHECK(result.first[1].execRes.excepted == dev::eth::TransactionException::None); + BOOST_CHECK(result.first[0].execRes.gasUsed == 21688); + BOOST_CHECK(result.first[1].execRes.gasUsed == 22179); + BOOST_CHECK(result.first[0].execRes.output.size() == 0); + BOOST_CHECK(result.first[1].execRes.output.size() == 32); + BOOST_CHECK(dev::h256(result.first[1].execRes.output) == dev::h256(35)); + + // Create a transaction with an output that use transient storage which is expired. + txCancun.clear(); + txCancun.push_back(createQtumTransaction(getCode(CodeID::multiply_7), 0, GASLIMIT, dev::u256(1), ++hashTx, proxy)); + result = executeBC(txCancun, *m_node.chainman); + BOOST_CHECK(result.first[0].execRes.excepted == dev::eth::TransactionException::None); + BOOST_CHECK(result.first[0].execRes.gasUsed == 22179); + BOOST_CHECK(result.first[0].execRes.output.size() == 32); + BOOST_CHECK(dev::h256(result.first[0].execRes.output) == dev::h256(0)); +} + +BOOST_AUTO_TEST_CASE(checking_transient_storage_before_fork){ + genesisLoading(); + createNewBlocks(this, 498); + dev::h256 hashTx(HASHTX); + + // Create contract + std::vector txs; + txs.push_back(createQtumTransaction(getCode(CodeID::mulService), 0, GASLIMIT, dev::u256(1), ++hashTx, dev::Address())); + auto result = executeBC(txs, *m_node.chainman); + BOOST_CHECK(result.first[0].execRes.excepted == dev::eth::TransactionException::None); + + // Check that tstore and tload are bad instructions + dev::Address proxy = createQtumAddress(txs[0].getHashWith(), txs[0].getNVout()); + std::vector txCancun; + txCancun.push_back(createQtumTransaction(getCode(CodeID::setMultiplier_5), 0, GASLIMIT, dev::u256(1), ++hashTx, proxy)); + txCancun.push_back(createQtumTransaction(getCode(CodeID::multiply_7), 0, GASLIMIT, dev::u256(1), ++hashTx, proxy)); + result = executeBC(txCancun, *m_node.chainman); + BOOST_CHECK(result.first[0].execRes.excepted == dev::eth::TransactionException::BadInstruction); + BOOST_CHECK(result.first[1].execRes.excepted == dev::eth::TransactionException::BadInstruction); +} + +BOOST_AUTO_TEST_SUITE_END() + +}