1 /*
2 This file is part of solidity.
3
4 solidity is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 solidity is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with solidity. If not, see <http://www.gnu.org/licenses/>.
16 */
17 // SPDX-License-Identifier: GPL-3.0
18 /**
19 * Tests that check that the cost of certain operations stay within range.
20 */
21
22 #include <test/libsolidity/SolidityExecutionFramework.h>
23 #include <liblangutil/EVMVersion.h>
24 #include <libsolutil/IpfsHash.h>
25 #include <libevmasm/GasMeter.h>
26
27 #include <cmath>
28
29 using namespace std;
30 using namespace solidity::langutil;
31 using namespace solidity::langutil;
32 using namespace solidity::evmasm;
33 using namespace solidity::frontend;
34 using namespace solidity::test;
35
36 namespace solidity::frontend::test
37 {
38
39 #define CHECK_DEPLOY_GAS(_gasNoOpt, _gasOpt, _evmVersion) \
40 do \
41 { \
42 u256 metaCost = GasMeter::dataGas(m_compiler.cborMetadata(m_compiler.lastContractName()), true, _evmVersion); \
43 u256 gasOpt{_gasOpt}; \
44 u256 gasNoOpt{_gasNoOpt}; \
45 u256 gas = m_optimiserSettings == OptimiserSettings::minimal() ? gasNoOpt : gasOpt; \
46 BOOST_CHECK_MESSAGE( \
47 m_gasUsed >= metaCost, \
48 "Gas used: " + \
49 m_gasUsed.str() + \
50 " is less than the data cost for the cbor metadata: " + \
51 u256(metaCost).str() \
52 ); \
53 u256 gasUsed = m_gasUsed - metaCost; \
54 BOOST_CHECK_MESSAGE( \
55 gas == gasUsed, \
56 "Gas used: " + \
57 gasUsed.str() + \
58 " - expected: " + \
59 gas.str() \
60 ); \
61 } while(0)
62
63 #define CHECK_GAS(_gasNoOpt, _gasOpt, _tolerance) \
64 do \
65 { \
66 u256 gasOpt{_gasOpt}; \
67 u256 gasNoOpt{_gasNoOpt}; \
68 u256 tolerance{_tolerance}; \
69 u256 gas = m_optimiserSettings == OptimiserSettings::minimal() ? gasNoOpt : gasOpt; \
70 u256 diff = gas < m_gasUsed ? m_gasUsed - gas : gas - m_gasUsed; \
71 BOOST_CHECK_MESSAGE( \
72 diff <= tolerance, \
73 "Gas used: " + \
74 m_gasUsed.str() + \
75 " - expected: " + \
76 gas.str() + \
77 " (tolerance: " + \
78 tolerance.str() + \
79 ")" \
80 ); \
81 } while(0)
82
BOOST_FIXTURE_TEST_SUITE(GasCostTests,SolidityExecutionFramework)83 BOOST_FIXTURE_TEST_SUITE(GasCostTests, SolidityExecutionFramework)
84
85 BOOST_AUTO_TEST_CASE(string_storage)
86 {
87 char const* sourceCode = R"(
88 contract C {
89 function f() pure public {
90 require(false, "Not Authorized. This function can only be called by the custodian or owner of this contract");
91 }
92 }
93 )";
94 m_compiler.setMetadataFormat(CompilerStack::MetadataFormat::NoMetadata);
95 compileAndRun(sourceCode);
96
97 auto evmVersion = solidity::test::CommonOptions::get().evmVersion();
98
99 if (evmVersion <= EVMVersion::byzantium())
100 {
101 if (CommonOptions::get().useABIEncoderV1)
102 CHECK_DEPLOY_GAS(133045, 129731, evmVersion);
103 else
104 CHECK_DEPLOY_GAS(144999, 121229, evmVersion);
105 }
106 // This is only correct on >=Constantinople.
107 else if (!CommonOptions::get().useABIEncoderV1)
108 {
109 if (CommonOptions::get().optimize)
110 {
111 // Costs with 0 are cases which cannot be triggered in tests.
112 if (evmVersion < EVMVersion::istanbul())
113 CHECK_DEPLOY_GAS(0, 109241, evmVersion);
114 else
115 CHECK_DEPLOY_GAS(0, 97697, evmVersion);
116 }
117 else
118 {
119 if (evmVersion < EVMVersion::istanbul())
120 CHECK_DEPLOY_GAS(139013, 123969, evmVersion);
121 else
122 CHECK_DEPLOY_GAS(123361, 110969, evmVersion);
123 }
124 }
125 else if (evmVersion < EVMVersion::istanbul())
126 CHECK_DEPLOY_GAS(125829, 118559, evmVersion);
127 else
128 CHECK_DEPLOY_GAS(114077, 96461, evmVersion);
129
130 if (evmVersion >= EVMVersion::byzantium())
131 {
132 callContractFunction("f()");
133 if (evmVersion == EVMVersion::byzantium())
134 CHECK_GAS(21741, 21522, 20);
135 // This is only correct on >=Constantinople.
136 else if (!CommonOptions::get().useABIEncoderV1)
137 {
138 if (CommonOptions::get().optimize)
139 {
140 if (evmVersion < EVMVersion::istanbul())
141 CHECK_GAS(0, 21526, 20);
142 else
143 CHECK_GAS(0, 21318, 20);
144 }
145 else
146 {
147 if (evmVersion < EVMVersion::istanbul())
148 CHECK_GAS(21736, 21559, 20);
149 else
150 CHECK_GAS(21528, 21351, 20);
151 }
152 }
153 else if (evmVersion < EVMVersion::istanbul())
154 CHECK_GAS(21546, 21526, 20);
155 else
156 CHECK_GAS(21332, 21322, 20);
157 }
158 }
159
BOOST_AUTO_TEST_CASE(single_callvaluecheck)160 BOOST_AUTO_TEST_CASE(single_callvaluecheck)
161 {
162 string sourceCode = R"(
163 // All functions nonpayable, we can check callvalue at the beginning
164 contract Nonpayable {
165 address a;
166 function f(address b) public {
167 a = b;
168 }
169 function f1(address b) public pure returns (uint c) {
170 return uint160(b) + 2;
171 }
172 function f2(address b) public pure returns (uint) {
173 return uint160(b) + 8;
174 }
175 function f3(address, uint c) pure public returns (uint) {
176 return c - 5;
177 }
178 }
179 // At least on payable function, we cannot do the optimization.
180 contract Payable {
181 address a;
182 function f(address b) public {
183 a = b;
184 }
185 function f1(address b) public pure returns (uint c) {
186 return uint160(b) + 2;
187 }
188 function f2(address b) public pure returns (uint) {
189 return uint160(b) + 8;
190 }
191 function f3(address, uint c) payable public returns (uint) {
192 return c - 5;
193 }
194 }
195 )";
196 compileAndRun(sourceCode);
197 size_t bytecodeSizeNonpayable = m_compiler.object("Nonpayable").bytecode.size();
198 size_t bytecodeSizePayable = m_compiler.object("Payable").bytecode.size();
199
200 BOOST_CHECK_EQUAL(bytecodeSizePayable - bytecodeSizeNonpayable, 26);
201 }
202
203 BOOST_AUTO_TEST_SUITE_END()
204
205 }
206