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  * @author Alex Beregszaszi
20  * @date 2018
21  * Tests for the assembler.
22  */
23 
24 #include <libevmasm/Assembly.h>
25 #include <libsolutil/JSON.h>
26 #include <libyul/Exceptions.h>
27 
28 #include <boost/test/unit_test.hpp>
29 
30 #include <algorithm>
31 #include <memory>
32 #include <string>
33 #include <tuple>
34 
35 using namespace std;
36 using namespace solidity::langutil;
37 using namespace solidity::evmasm;
38 
39 namespace solidity::frontend::test
40 {
41 
42 namespace
43 {
checkCompilation(evmasm::Assembly const & _assembly)44 	void checkCompilation(evmasm::Assembly const& _assembly)
45 	{
46 		LinkerObject output = _assembly.assemble();
47 		BOOST_CHECK(output.bytecode.size() > 0);
48 		BOOST_CHECK(output.toHex().length() > 0);
49 	}
50 }
51 
52 BOOST_AUTO_TEST_SUITE(Assembler)
53 
BOOST_AUTO_TEST_CASE(all_assembly_items)54 BOOST_AUTO_TEST_CASE(all_assembly_items)
55 {
56 	map<string, unsigned> indices = {
57 		{ "root.asm", 0 },
58 		{ "sub.asm", 1 }
59 	};
60 	Assembly _assembly;
61 	auto root_asm = make_shared<string>("root.asm");
62 	_assembly.setSourceLocation({1, 3, root_asm});
63 
64 	Assembly _subAsm;
65 	auto sub_asm = make_shared<string>("sub.asm");
66 	_subAsm.setSourceLocation({6, 8, sub_asm});
67 	// PushImmutable
68 	_subAsm.appendImmutable("someImmutable");
69 	_subAsm.append(Instruction::INVALID);
70 	shared_ptr<Assembly> _subAsmPtr = make_shared<Assembly>(_subAsm);
71 
72 	// Tag
73 	auto tag = _assembly.newTag();
74 	_assembly.append(tag);
75 	// Operation
76 	_assembly.append(u256(1));
77 	_assembly.append(u256(2));
78 	// Push
79 	_assembly.append(Instruction::KECCAK256);
80 	// PushProgramSize
81 	_assembly.appendProgramSize();
82 	// PushLibraryAddress
83 	_assembly.appendLibraryAddress("someLibrary");
84 	// PushTag + Operation
85 	_assembly.appendJump(tag);
86 	// PushData
87 	_assembly.append(bytes{0x1, 0x2, 0x3, 0x4});
88 	// PushSubSize
89 	auto sub = _assembly.appendSubroutine(_subAsmPtr);
90 	// PushSub
91 	_assembly.pushSubroutineOffset(static_cast<size_t>(sub.data()));
92 	// PushDeployTimeAddress
93 	_assembly.append(PushDeployTimeAddress);
94 	// AssignImmutable.
95 	// Note that since there is no reference to "someOtherImmutable", this will just compile to two POPs in the hex output.
96 	_assembly.appendImmutableAssignment("someOtherImmutable");
97 	_assembly.append(u256(2));
98 	_assembly.appendImmutableAssignment("someImmutable");
99 	// Operation
100 	_assembly.append(Instruction::STOP);
101 	_assembly.appendToAuxiliaryData(bytes{0x42, 0x66});
102 	_assembly.appendToAuxiliaryData(bytes{0xee, 0xaa});
103 
104 	checkCompilation(_assembly);
105 
106 	BOOST_CHECK_EQUAL(
107 		_assembly.assemble().toHex(),
108 		"5b6001600220606f73__$bf005014d9d0f534b8fcb268bd84c491a2$__"
109 		"6000566067602260457300000000000000000000000000000000000000005050"
110 		"600260010152"
111 		"00fe"
112 		"7f0000000000000000000000000000000000000000000000000000000000000000"
113 		"fe010203044266eeaa"
114 	);
115 	BOOST_CHECK_EQUAL(
116 		_assembly.assemblyString(),
117 		"    /* \"root.asm\":1:3   */\n"
118 		"tag_1:\n"
119 		"  keccak256(0x02, 0x01)\n"
120 		"  bytecodeSize\n"
121 		"  linkerSymbol(\"bf005014d9d0f534b8fcb268bd84c491a2380f4acd260d1ccfe9cd8201f7e994\")\n"
122 		"  jump(tag_1)\n"
123 		"  data_a6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b\n"
124 		"  dataSize(sub_0)\n"
125 		"  dataOffset(sub_0)\n"
126 		"  deployTimeAddress()\n"
127 		"  assignImmutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n"
128 		"  0x02\n"
129 		"  assignImmutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n"
130 		"  stop\n"
131 		"stop\n"
132 		"data_a6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b 01020304\n"
133 		"\n"
134 		"sub_0: assembly {\n"
135 		"        /* \"sub.asm\":6:8   */\n"
136 		"      immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n"
137 		"      invalid\n"
138 		"}\n"
139 		"\n"
140 		"auxdata: 0x4266eeaa\n"
141 	);
142 	BOOST_CHECK_EQUAL(
143 		util::jsonCompactPrint(_assembly.assemblyJSON(indices)),
144 		"{\".auxdata\":\"4266eeaa\",\".code\":["
145 		"{\"begin\":1,\"end\":3,\"name\":\"tag\",\"source\":0,\"value\":\"1\"},"
146 		"{\"begin\":1,\"end\":3,\"name\":\"JUMPDEST\",\"source\":0},"
147 		"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"1\"},"
148 		"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2\"},"
149 		"{\"begin\":1,\"end\":3,\"name\":\"KECCAK256\",\"source\":0},"
150 		"{\"begin\":1,\"end\":3,\"name\":\"PUSHSIZE\",\"source\":0},"
151 		"{\"begin\":1,\"end\":3,\"name\":\"PUSHLIB\",\"source\":0,\"value\":\"someLibrary\"},"
152 		"{\"begin\":1,\"end\":3,\"name\":\"PUSH [tag]\",\"source\":0,\"value\":\"1\"},"
153 		"{\"begin\":1,\"end\":3,\"name\":\"JUMP\",\"source\":0},"
154 		"{\"begin\":1,\"end\":3,\"name\":\"PUSH data\",\"source\":0,\"value\":\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\"},"
155 		"{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
156 		"{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
157 		"{\"begin\":1,\"end\":3,\"name\":\"PUSHDEPLOYADDRESS\",\"source\":0},"
158 		"{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someOtherImmutable\"},"
159 		"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2\"},"
160 		"{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someImmutable\"},"
161 		"{\"begin\":1,\"end\":3,\"name\":\"STOP\",\"source\":0}"
162 		"],\".data\":{\"0\":{\".code\":["
163 		"{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"},"
164 		"{\"begin\":6,\"end\":8,\"name\":\"INVALID\",\"source\":1}"
165 		"]},\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"}}"
166 	);
167 }
168 
BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)169 BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
170 {
171 	// Tests for 1, 2, 3 number of immutables.
172 	for (int numImmutables = 1; numImmutables <= 3; ++numImmutables)
173 	{
174 		BOOST_TEST_MESSAGE("NumImmutables: "s + to_string(numImmutables));
175 		// Tests for the cases 1, 2, 3 occurrences of an immutable reference.
176 		for (int numActualRefs = 1; numActualRefs <= 3; ++numActualRefs)
177 		{
178 			BOOST_TEST_MESSAGE("NumActualRefs: "s + to_string(numActualRefs));
179 			auto const NumExpectedMappings =
180 				(
181 					2 +                        // PUSH <a> PUSH <b>
182 					(numActualRefs - 1) * 5 +  // DUP DUP PUSH <n> ADD MTOSRE
183 					3                          // PUSH <n> ADD MSTORkhbE
184 				) * numImmutables;
185 
186 			auto constexpr NumSubs = 1;
187 			auto constexpr NumOpcodesWithoutMappings =
188 				NumSubs +                  // PUSH <addr> for every sub assembly
189 				1;                         // INVALID
190 
191 			auto assemblyName = make_shared<string>("root.asm");
192 			auto subName = make_shared<string>("sub.asm");
193 
194 			map<string, unsigned> indices = {
195 				{ *assemblyName, 0 },
196 				{ *subName, 1 }
197 			};
198 
199 			auto subAsm = make_shared<Assembly>();
200 			for (char i = 0; i < numImmutables; ++i)
201 			{
202 				for (int r = 0; r < numActualRefs; ++r)
203 				{
204 					subAsm->setSourceLocation(SourceLocation{10*i, 10*i + 6 + r, subName});
205 					subAsm->appendImmutable(string(1, char('a' + i))); // "a", "b", ...
206 				}
207 			}
208 
209 			Assembly assembly;
210 			for (char i = 1; i <= numImmutables; ++i)
211 			{
212 				assembly.setSourceLocation({10*i, 10*i + 3+i, assemblyName});
213 				assembly.append(u256(0x71));              // immutble value
214 				assembly.append(u256(0));                 // target... modules?
215 				assembly.appendImmutableAssignment(string(1, char('a' + i - 1)));
216 			}
217 
218 			assembly.appendSubroutine(subAsm);
219 
220 			checkCompilation(assembly);
221 
222 			string const sourceMappings = AssemblyItem::computeSourceMapping(assembly.items(), indices);
223 			auto const numberOfMappings = std::count(sourceMappings.begin(), sourceMappings.end(), ';');
224 
225 			LinkerObject const& obj = assembly.assemble();
226 			string const disassembly = disassemble(obj.bytecode, "\n");
227 			auto const numberOfOpcodes = std::count(disassembly.begin(), disassembly.end(), '\n');
228 
229 			#if 0 // {{{ debug prints
230 			{
231 				LinkerObject const& subObj = assembly.sub(0).assemble();
232 				string const subDisassembly = disassemble(subObj.bytecode, "\n");
233 				cout << '\n';
234 				cout << "### immutables: " << numImmutables << ", refs: " << numActualRefs << '\n';
235 				cout << " - srcmap: \"" << sourceMappings << "\"\n";
236 				cout << " - src mappings: " << numberOfMappings << '\n';
237 				cout << " - opcodes: " << numberOfOpcodes << '\n';
238 				cout << " - subs: " << assembly.numSubs() << '\n';
239 				cout << " - sub opcodes " << std::count(subDisassembly.begin(), subDisassembly.end(), '\n') << '\n';
240 				cout << " - sub srcmaps " << AssemblyItem::computeSourceMapping(subAsm->items(), indices) << '\n';
241 				cout << " - main bytecode:\n\t" << disassemble(obj.bytecode, "\n\t");
242 				cout << "\r - sub bytecode:\n\t" << disassemble(subObj.bytecode, "\n\t");
243 			}
244 			#endif // }}}
245 
246 			BOOST_REQUIRE_EQUAL(NumExpectedMappings, numberOfMappings);
247 			BOOST_REQUIRE_EQUAL(NumExpectedMappings, numberOfOpcodes - NumOpcodesWithoutMappings);
248 		}
249 	}
250 }
251 
BOOST_AUTO_TEST_CASE(immutable)252 BOOST_AUTO_TEST_CASE(immutable)
253 {
254 	map<string, unsigned> indices = {
255 		{ "root.asm", 0 },
256 		{ "sub.asm", 1 }
257 	};
258 	Assembly _assembly;
259 	auto root_asm = make_shared<string>("root.asm");
260 	_assembly.setSourceLocation({1, 3, root_asm});
261 
262 	Assembly _subAsm;
263 	auto sub_asm = make_shared<string>("sub.asm");
264 	_subAsm.setSourceLocation({6, 8, sub_asm});
265 	_subAsm.appendImmutable("someImmutable");
266 	_subAsm.appendImmutable("someOtherImmutable");
267 	_subAsm.appendImmutable("someImmutable");
268 	shared_ptr<Assembly> _subAsmPtr = make_shared<Assembly>(_subAsm);
269 
270 	_assembly.append(u256(42));
271 	_assembly.append(u256(0));
272 	_assembly.appendImmutableAssignment("someImmutable");
273 	_assembly.append(u256(23));
274 	_assembly.append(u256(0));
275 	_assembly.appendImmutableAssignment("someOtherImmutable");
276 
277 	auto sub = _assembly.appendSubroutine(_subAsmPtr);
278 	_assembly.pushSubroutineOffset(static_cast<size_t>(sub.data()));
279 
280 	checkCompilation(_assembly);
281 
282 	BOOST_CHECK_EQUAL(
283 		_assembly.assemble().toHex(),
284 		// root.asm
285 		// assign "someImmutable"
286 		"602a" // PUSH1 42 - value for someImmutable
287 		"6000" // PUSH1 0 - offset of code into which to insert the immutable
288 		"8181" // DUP2 DUP2
289 		"6001" // PUSH1 1 - offset of first someImmutable in sub_0
290 		"01" // ADD - add offset of immutable to offset of code
291 		"52" // MSTORE
292 		"6043" // PUSH1 67 - offset of second someImmutable in sub_0
293 		"01" // ADD - add offset of immutable to offset of code
294 		"52" // MSTORE
295 		// assign "someOtherImmutable"
296 		"6017" // PUSH1 23 - value for someOtherImmutable
297 		"6000" // PUSH1 0 - offset of code into which to insert the immutable
298 		"6022" // PUSH1 34 - offset of someOtherImmutable in sub_0
299 		"01" // ADD - add offset of immutable to offset of code
300 		"52" // MSTORE
301 		"6063" // PUSH1 0x63 - dataSize(sub_0)
302 		"601b" // PUSH1 0x23 - dataOffset(sub_0)
303 		"fe" // INVALID
304 		// end of root.asm
305 		// sub.asm
306 		"7f0000000000000000000000000000000000000000000000000000000000000000" // PUSHIMMUTABLE someImmutable - data at offset 1
307 		"7f0000000000000000000000000000000000000000000000000000000000000000" // PUSHIMMUTABLE someOtherImmutable - data at offset 34
308 		"7f0000000000000000000000000000000000000000000000000000000000000000" // PUSHIMMUTABLE someImmutable - data at offset 67
309 	);
310 	BOOST_CHECK_EQUAL(
311 		_assembly.assemblyString(),
312 		"    /* \"root.asm\":1:3   */\n"
313 		"  0x2a\n"
314 		"  0x00\n"
315 		"  assignImmutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n"
316 		"  0x17\n"
317 		"  0x00\n"
318 		"  assignImmutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n"
319 		"  dataSize(sub_0)\n"
320 		"  dataOffset(sub_0)\n"
321 		"stop\n"
322 		"\n"
323 		"sub_0: assembly {\n"
324 		"        /* \"sub.asm\":6:8   */\n"
325 		"      immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n"
326 		"      immutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n"
327 		"      immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n"
328 		"}\n"
329 	);
330 	BOOST_CHECK_EQUAL(
331 		util::jsonCompactPrint(_assembly.assemblyJSON(indices)),
332 		"{\".code\":["
333 		"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2A\"},"
334 		"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"},"
335 		"{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someImmutable\"},"
336 		"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"17\"},"
337 		"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"},"
338 		"{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someOtherImmutable\"},"
339 		"{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
340 		"{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}"
341 		"],\".data\":{\"0\":{\".code\":["
342 		"{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"},"
343 		"{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someOtherImmutable\"},"
344 		"{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"}"
345 		"]}}}"
346 	);
347 }
348 
BOOST_AUTO_TEST_CASE(subobject_encode_decode)349 BOOST_AUTO_TEST_CASE(subobject_encode_decode)
350 {
351 	Assembly assembly;
352 
353 	shared_ptr<Assembly> subAsmPtr = make_shared<Assembly>();
354 	shared_ptr<Assembly> subSubAsmPtr = make_shared<Assembly>();
355 
356 	assembly.appendSubroutine(subAsmPtr);
357 	subAsmPtr->appendSubroutine(subSubAsmPtr);
358 
359 	BOOST_CHECK(assembly.encodeSubPath({0}) == 0);
360 	BOOST_REQUIRE_THROW(assembly.encodeSubPath({1}), solidity::evmasm::AssemblyException);
361 	BOOST_REQUIRE_THROW(assembly.decodeSubPath(1), solidity::evmasm::AssemblyException);
362 
363 	vector<size_t> subPath{0, 0};
364 	BOOST_CHECK(assembly.decodeSubPath(assembly.encodeSubPath(subPath)) == subPath);
365 }
366 
367 BOOST_AUTO_TEST_SUITE_END()
368 
369 } // end namespaces
370