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