Skip to content

Commit 052b98c

Browse files
author
timemarkovqtum
committed
Add transient storage unit tests
1 parent bbc621e commit 052b98c

File tree

2 files changed

+149
-1
lines changed

2 files changed

+149
-1
lines changed

src/Makefile.test.include

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,8 @@ BITCOIN_TESTS =\
198198
test/qtumtests/istanbulfork_tests.cpp \
199199
test/qtumtests/londonfork_tests.cpp \
200200
test/qtumtests/evmone_tests.cpp \
201-
test/qtumtests/shanghaifork_tests.cpp
201+
test/qtumtests/shanghaifork_tests.cpp \
202+
test/qtumtests/cancunfork_tests.cpp
202203

203204
if ENABLE_WALLET
204205
BITCOIN_TESTS += \
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
#include <boost/test/unit_test.hpp>
2+
#include <qtumtests/test_utils.h>
3+
#include <qtum/qtumutils.h>
4+
#include <chainparams.h>
5+
6+
namespace CancunTest{
7+
8+
const dev::u256 GASLIMIT = dev::u256(500000);
9+
const dev::h256 HASHTX = dev::h256(ParseHex("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
10+
11+
// Codes used to check that cancun fork
12+
const std::vector<valtype> CODE = {
13+
/*
14+
pragma solidity ^0.8.24;
15+
16+
contract MulService {
17+
function setMultiplier(uint multiplier) external {
18+
assembly {
19+
tstore(0, multiplier)
20+
}
21+
}
22+
23+
function getMultiplier() private view returns (uint multiplier) {
24+
assembly {
25+
multiplier := tload(0)
26+
}
27+
}
28+
29+
function multiply(uint value) external view returns (uint) {
30+
return value * getMultiplier();
31+
}
32+
}
33+
*/ valtype(ParseHex("6080604052348015600e575f80fd5b506101db8061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c8063641579a614610038578063c6888fa114610054575b5f80fd5b610052600480360381019061004d91906100e4565b610084565b005b61006e600480360381019061006991906100e4565b61008a565b60405161007b919061011e565b60405180910390f35b805f5d50565b5f6100936100a5565b8261009e9190610164565b9050919050565b5f805c905090565b5f80fd5b5f819050919050565b6100c3816100b1565b81146100cd575f80fd5b50565b5f813590506100de816100ba565b92915050565b5f602082840312156100f9576100f86100ad565b5b5f610106848285016100d0565b91505092915050565b610118816100b1565b82525050565b5f6020820190506101315f83018461010f565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61016e826100b1565b9150610179836100b1565b9250828202610187816100b1565b9150828204841483151761019e5761019d610137565b5b509291505056fea26469706673582212203dda524f45e82e20c7a5140d36ef2410c23bec4dee31ca21bc368cf508cde0f764736f6c634300081a0033")),
34+
// setMultiplier(5)
35+
valtype(ParseHex("641579a60000000000000000000000000000000000000000000000000000000000000005")),
36+
// multiply(7)
37+
valtype(ParseHex("c6888fa10000000000000000000000000000000000000000000000000000000000000007")),
38+
};
39+
40+
// Codes IDs used to check that london fork is present
41+
enum class CodeID
42+
{
43+
mulService = 0,
44+
setMultiplier_5,
45+
multiply_7,
46+
};
47+
48+
// Get the code identified by the ID
49+
valtype getCode(CodeID id)
50+
{
51+
return CODE[(int)id];
52+
}
53+
54+
void genesisLoading(){
55+
const CChainParams& chainparams = Params();
56+
int coinbaseMaturity = Params().GetConsensus().CoinbaseMaturity(0);
57+
int forkHeight = coinbaseMaturity + 499;
58+
dev::eth::EVMConsensus evmConsensus;
59+
evmConsensus.QIP6Height = coinbaseMaturity;
60+
evmConsensus.QIP7Height = coinbaseMaturity;
61+
evmConsensus.nMuirGlacierHeight = coinbaseMaturity;
62+
evmConsensus.nLondonHeight = coinbaseMaturity;
63+
evmConsensus.nShanghaiHeight = coinbaseMaturity;
64+
evmConsensus.nCancunHeight = forkHeight;
65+
UpdateCancunHeight(forkHeight);
66+
dev::eth::ChainParams cp(chainparams.EVMGenesisInfo(evmConsensus));
67+
globalState->populateFrom(cp.genesisState);
68+
globalSealEngine = std::unique_ptr<dev::eth::SealEngineFace>(cp.createSealEngine());
69+
globalState->db().commit();
70+
}
71+
72+
void createNewBlocks(TestChain100Setup* testChain100Setup, size_t n){
73+
std::function<void(size_t n)> generateBlocks = [&](size_t n){
74+
dev::h256 oldHashStateRoot = globalState->rootHash();
75+
dev::h256 oldHashUTXORoot = globalState->rootHashUTXO();
76+
for(size_t i = 0; i < n; i++){
77+
testChain100Setup->CreateAndProcessBlock({}, GetScriptForRawPubKey(testChain100Setup->coinbaseKey.GetPubKey()));
78+
}
79+
globalState->setRoot(oldHashStateRoot);
80+
globalState->setRootUTXO(oldHashUTXORoot);
81+
};
82+
83+
generateBlocks(n);
84+
}
85+
BOOST_FIXTURE_TEST_SUITE(cancunfork_tests, TestChain100Setup)
86+
87+
BOOST_AUTO_TEST_CASE(checking_transient_storage_after_fork){
88+
genesisLoading();
89+
createNewBlocks(this, 499);
90+
dev::h256 hashTx(HASHTX);
91+
92+
// Create contract
93+
std::vector<QtumTransaction> txs;
94+
txs.push_back(createQtumTransaction(getCode(CodeID::mulService), 0, GASLIMIT, dev::u256(1), ++hashTx, dev::Address()));
95+
auto result = executeBC(txs, *m_node.chainman);
96+
BOOST_CHECK(result.first[0].execRes.excepted == dev::eth::TransactionException::None);
97+
98+
// Create a transaction with 2 outputs that use transient storage.
99+
// The first output set the multiplier to 5.
100+
// The second output multiply the multiplier by 7, and the result is 35.
101+
dev::Address proxy = createQtumAddress(txs[0].getHashWith(), txs[0].getNVout());
102+
std::vector<QtumTransaction> txCancun;
103+
txCancun.push_back(createQtumTransaction(getCode(CodeID::setMultiplier_5), 0, GASLIMIT, dev::u256(1), ++hashTx, proxy));
104+
txCancun.push_back(createQtumTransaction(getCode(CodeID::multiply_7), 0, GASLIMIT, dev::u256(1), ++hashTx, proxy));
105+
result = executeBC(txCancun, *m_node.chainman);
106+
BOOST_CHECK(result.first[0].execRes.excepted == dev::eth::TransactionException::None);
107+
BOOST_CHECK(result.first[1].execRes.excepted == dev::eth::TransactionException::None);
108+
BOOST_CHECK(result.first[0].execRes.gasUsed == 21688);
109+
BOOST_CHECK(result.first[1].execRes.gasUsed == 22179);
110+
BOOST_CHECK(result.first[0].execRes.output.size() == 0);
111+
BOOST_CHECK(result.first[1].execRes.output.size() == 32);
112+
BOOST_CHECK(dev::h256(result.first[1].execRes.output) == dev::h256(35));
113+
114+
// Create a transaction with an output that use transient storage which is expired.
115+
txCancun.clear();
116+
txCancun.push_back(createQtumTransaction(getCode(CodeID::multiply_7), 0, GASLIMIT, dev::u256(1), ++hashTx, proxy));
117+
result = executeBC(txCancun, *m_node.chainman);
118+
BOOST_CHECK(result.first[0].execRes.excepted == dev::eth::TransactionException::None);
119+
BOOST_CHECK(result.first[0].execRes.gasUsed == 22179);
120+
BOOST_CHECK(result.first[0].execRes.output.size() == 32);
121+
BOOST_CHECK(dev::h256(result.first[0].execRes.output) == dev::h256(0));
122+
}
123+
124+
BOOST_AUTO_TEST_CASE(checking_transient_storage_before_fork){
125+
genesisLoading();
126+
createNewBlocks(this, 498);
127+
dev::h256 hashTx(HASHTX);
128+
129+
// Create contract
130+
std::vector<QtumTransaction> txs;
131+
txs.push_back(createQtumTransaction(getCode(CodeID::mulService), 0, GASLIMIT, dev::u256(1), ++hashTx, dev::Address()));
132+
auto result = executeBC(txs, *m_node.chainman);
133+
BOOST_CHECK(result.first[0].execRes.excepted == dev::eth::TransactionException::None);
134+
135+
// Check that tstore and tload are bad instructions
136+
dev::Address proxy = createQtumAddress(txs[0].getHashWith(), txs[0].getNVout());
137+
std::vector<QtumTransaction> txCancun;
138+
txCancun.push_back(createQtumTransaction(getCode(CodeID::setMultiplier_5), 0, GASLIMIT, dev::u256(1), ++hashTx, proxy));
139+
txCancun.push_back(createQtumTransaction(getCode(CodeID::multiply_7), 0, GASLIMIT, dev::u256(1), ++hashTx, proxy));
140+
result = executeBC(txCancun, *m_node.chainman);
141+
BOOST_CHECK(result.first[0].execRes.excepted == dev::eth::TransactionException::BadInstruction);
142+
BOOST_CHECK(result.first[1].execRes.excepted == dev::eth::TransactionException::BadInstruction);
143+
}
144+
145+
BOOST_AUTO_TEST_SUITE_END()
146+
147+
}

0 commit comments

Comments
 (0)