mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	- updated func/fift - additional checks in block validator - docs - tunnel prototype in ADNL
		
			
				
	
	
		
			450 lines
		
	
	
	
		
			8.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			450 lines
		
	
	
	
		
			8.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|     This file is part of TON Blockchain Library.
 | |
| 
 | |
|     TON Blockchain Library is free software: you can redistribute it and/or modify
 | |
|     it under the terms of the GNU Lesser General Public License as published by
 | |
|     the Free Software Foundation, either version 2 of the License, or
 | |
|     (at your option) any later version.
 | |
| 
 | |
|     TON Blockchain Library is distributed in the hope that it will be useful,
 | |
|     but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|     GNU Lesser General Public License for more details.
 | |
| 
 | |
|     You should have received a copy of the GNU Lesser General Public License
 | |
|     along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | |
| 
 | |
|     Copyright 2017-2020 Telegram Systems LLP
 | |
| */
 | |
| #include "vm/vm.h"
 | |
| #include "vm/cp0.h"
 | |
| #include "vm/dict.h"
 | |
| #include "fift/utils.h"
 | |
| #include "common/bigint.hpp"
 | |
| 
 | |
| #include "td/utils/base64.h"
 | |
| #include "td/utils/tests.h"
 | |
| #include "td/utils/ScopeGuard.h"
 | |
| #include "td/utils/StringBuilder.h"
 | |
| 
 | |
| std::string run_vm(td::Ref<vm::Cell> cell) {
 | |
|   vm::init_op_cp0();
 | |
|   vm::DictionaryBase::get_empty_dictionary();
 | |
| 
 | |
|   class Logger : public td::LogInterface {
 | |
|    public:
 | |
|     void append(td::CSlice slice) override {
 | |
|       res.append(slice.data(), slice.size());
 | |
|     }
 | |
|     std::string res;
 | |
|   };
 | |
|   static Logger logger;
 | |
|   logger.res = "";
 | |
|   td::set_log_fatal_error_callback([](td::CSlice message) { td::default_log_interface->append(logger.res); });
 | |
|   vm::VmLog log{&logger, td::LogOptions::plain()};
 | |
|   log.log_options.level = 4;
 | |
|   log.log_options.fix_newlines = true;
 | |
|   log.log_mask |= vm::VmLog::DumpStack;
 | |
| 
 | |
|   auto total_data_cells_before = vm::DataCell::get_total_data_cells();
 | |
|   SCOPE_EXIT {
 | |
|     auto total_data_cells_after = vm::DataCell::get_total_data_cells();
 | |
|     ASSERT_EQ(total_data_cells_before, total_data_cells_after);
 | |
|   };
 | |
| 
 | |
|   vm::Stack stack;
 | |
|   try {
 | |
|     vm::GasLimits gas_limit(1000, 1000);
 | |
| 
 | |
|     vm::run_vm_code(vm::load_cell_slice_ref(cell), stack, 0 /*flags*/, nullptr /*data*/, std::move(log) /*VmLog*/,
 | |
|                     nullptr, &gas_limit);
 | |
|   } catch (...) {
 | |
|     LOG(FATAL) << "catch unhandled exception";
 | |
|   }
 | |
|   return logger.res;  // must be a copy
 | |
| }
 | |
| 
 | |
| td::Ref<vm::Cell> to_cell(const unsigned char *buff, int bits) {
 | |
|   return vm::CellBuilder().store_bits(buff, bits, 0).finalize();
 | |
| }
 | |
| void test_run_vm(td::Ref<vm::Cell> code) {
 | |
|   auto a = run_vm(code);
 | |
|   auto b = run_vm(code);
 | |
|   ASSERT_EQ(a, b);
 | |
|   REGRESSION_VERIFY(a);
 | |
| }
 | |
| 
 | |
| void test_run_vm(td::Slice code_hex) {
 | |
|   unsigned char buff[128];
 | |
|   int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), code_hex.begin(), code_hex.end());
 | |
|   CHECK(bits >= 0);
 | |
|   test_run_vm(to_cell(buff, bits));
 | |
| }
 | |
| 
 | |
| void test_run_vm_raw(td::Slice code64) {
 | |
|   auto code = td::base64_decode(code64).move_as_ok();
 | |
|   if (code.size() > 127) {
 | |
|     code.resize(127);
 | |
|   }
 | |
|   test_run_vm(vm::CellBuilder().store_bytes(code).finalize());
 | |
| }
 | |
| 
 | |
| TEST(VM, simple) {
 | |
|   test_run_vm("ABCBABABABA");
 | |
| }
 | |
| 
 | |
| TEST(VM, memory_leak_old) {
 | |
|   test_run_vm("90787FDB3B");
 | |
| }
 | |
| 
 | |
| TEST(VM, memory_leak) {
 | |
|   test_run_vm("90707FDB3B");
 | |
| }
 | |
| 
 | |
| TEST(VM, bug_div_short_any) {
 | |
|   test_run_vm("6883FF73A98D");
 | |
| }
 | |
| TEST(VM, assert_pfx_dict_lookup) {
 | |
|   test_run_vm("778B04216D73F43E018B04591277F473");
 | |
| }
 | |
| 
 | |
| TEST(VM, assert_lookup_prefix) {
 | |
|   test_run_vm("78E58B008B028B04010000016D90ED5272F43A755D77F4A8");
 | |
| }
 | |
| 
 | |
| TEST(VM, assert_code_not_null) {
 | |
|   test_run_vm("76ED40DE");
 | |
| }
 | |
| 
 | |
| TEST(VM, bug_exec_dict_getnear) {
 | |
|   test_run_vm("8B048B00006D72F47573655F6D656D6D656D8B007F");
 | |
| }
 | |
| 
 | |
| TEST(VM, bug_stack_overflow) {
 | |
|   test_run_vm("72A93AF8");
 | |
| }
 | |
| 
 | |
| TEST(VM, assert_extract_minmax_key) {
 | |
|   test_run_vm("6D6DEB21807AF49C2180EB21807AF41C");
 | |
| }
 | |
| 
 | |
| TEST(VM, memory_leak_new) {
 | |
|   test_run_vm("72E5ED40DB3603");
 | |
| }
 | |
| 
 | |
| TEST(VM, unhandled_exception_1) {
 | |
|   test_run_vm("70EDA2ED00");
 | |
| }
 | |
| 
 | |
| TEST(VM, unhandled_exception_2) {
 | |
|   // infinite loop now
 | |
|   test_run_vm("EBEDB4");
 | |
| }
 | |
| 
 | |
| TEST(VM, unhandled_exception_3) {
 | |
|   // infinite loop now
 | |
|   test_run_vm("EBEDC0");
 | |
| }
 | |
| 
 | |
| TEST(VM, unhandled_exception_4) {
 | |
|   test_run_vm("7F853EA1C8CB3E");
 | |
| }
 | |
| 
 | |
| TEST(VM, unhandled_exception_5) {
 | |
|   test_run_vm("738B04016D21F41476A721F49F");
 | |
| }
 | |
| 
 | |
| TEST(VM, infinity_loop_1) {
 | |
|   test_run_vm_raw("f3r4AJGQ6rDraIQ=");
 | |
| }
 | |
| TEST(VM, infinity_loop_2) {
 | |
|   test_run_vm_raw("kpTt7ZLrig==");
 | |
| }
 | |
| 
 | |
| TEST(VM, oom_1) {
 | |
|   test_run_vm_raw("bXflX/BvDw==");
 | |
| }
 | |
| 
 | |
| TEST(VM, bigint) {
 | |
|   td::StringBuilder sb({}, true);
 | |
| 
 | |
|   using word_t = td::BigIntInfo::word_t;
 | |
|   std::vector<word_t> numbers{1,
 | |
|                               -1,
 | |
|                               2,
 | |
|                               -2,
 | |
|                               100,
 | |
|                               -100,
 | |
|                               std::numeric_limits<word_t>::max(),
 | |
|                               std::numeric_limits<word_t>::min(),
 | |
|                               std::numeric_limits<word_t>::max() - 1,
 | |
|                               std::numeric_limits<word_t>::min() + 1};
 | |
|   for (auto x : numbers) {
 | |
|     for (auto y : numbers) {
 | |
|       word_t a;
 | |
|       word_t b;
 | |
|       td::BigIntInfo::set_mul(&a, &b, x, y);
 | |
|       sb << "set_mul " << x << " * " << y << " = " << a << " " << b << "\n";
 | |
|       td::BigIntInfo::add_mul(&a, &b, x, y);
 | |
|       sb << "add_mul " << x << " " << y << " = " << a << " " << b << "\n";
 | |
|       td::BigIntInfo::sub_mul(&a, &b, x, y);
 | |
|       sb << "sub_mul " << x << " " << y << " = " << a << " " << b << "\n";
 | |
|     }
 | |
|   }
 | |
|   auto base = td::BigIntInfo::Base;
 | |
|   std::vector<word_t> lo_numbers{1, -1, 2, -2, 100, -100, base - 1, base - 2, -base + 1, -base + 2};
 | |
|   for (auto x : numbers) {
 | |
|     for (auto y : lo_numbers) {
 | |
|       for (auto z : numbers) {
 | |
|         word_t a;
 | |
|         word_t b;
 | |
|         td::BigIntInfo::dbl_divmod(&a, &b, x, y, z);
 | |
|         sb << "dbl_divmod " << x << " " << y << " / " << z << " = " << a << " " << b << "\n";
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   REGRESSION_VERIFY(sb.as_cslice());
 | |
| }
 | |
| 
 | |
| TEST(VM, report3_1) {
 | |
|   //WA: expect (1, 2, 6, 3)
 | |
|   td::Slice test1 =
 | |
|       R"A(
 | |
| CONT:<{
 | |
| DEPTH
 | |
| }>
 | |
| 3 SETNUMARGS
 | |
| c0 POPCTR
 | |
| 1 INT
 | |
| 2 INT
 | |
| 3 INT
 | |
| 4 INT
 | |
| 5 INT
 | |
| 6 INT
 | |
| 4 RETURNARGS
 | |
| )A";
 | |
|   test_run_vm(fift::compile_asm(test1).move_as_ok());
 | |
| }
 | |
| 
 | |
| TEST(VM, report3_2) {
 | |
|   td::Slice test1 =
 | |
|       R"A(
 | |
| CONT:<{
 | |
| DEPTH
 | |
| }>
 | |
| 2 SETNUMARGS
 | |
| c0 POPCTR
 | |
| 1 INT
 | |
| 2 INT
 | |
| 3 INT
 | |
| 4 INT
 | |
| 2 RETARGS
 | |
| )A";
 | |
|   test_run_vm(fift::compile_asm(test1).move_as_ok());
 | |
| }
 | |
| 
 | |
| TEST(VM, report3_3) {
 | |
|   // WA: expect (9)
 | |
|   td::Slice test1 =
 | |
|       R"A(
 | |
| CONT:<{
 | |
|  8 INT
 | |
| }>
 | |
| c0 POPCTR
 | |
| CONT:<{
 | |
|  9 INT
 | |
| }>
 | |
| c1 POPCTR
 | |
| 0 INT
 | |
| BRANCH
 | |
| )A";
 | |
|   test_run_vm(fift::compile_asm(test1).move_as_ok());
 | |
| }
 | |
| 
 | |
| TEST(VM, report3_4) {
 | |
|   td::Slice test1 =
 | |
|       R"A(
 | |
| CONT:<{
 | |
| 1 INT
 | |
| 2 INT
 | |
| 3 INT
 | |
| 2 RETARGS
 | |
| }>
 | |
| CALLX
 | |
| ADD
 | |
| )A";
 | |
|   test_run_vm(fift::compile_asm(test1).move_as_ok());
 | |
| }
 | |
| 
 | |
| TEST(VM, report3_6) {
 | |
|   // WA: expect StackOverflow
 | |
|   td::Slice test1 =
 | |
|       R"A(
 | |
| 10 INT
 | |
| 20 INT
 | |
| 30 INT
 | |
| CONT:<{
 | |
|   DEPTH
 | |
|   40 INT
 | |
|   SWAP
 | |
| }>
 | |
| 2 SETNUMARGS
 | |
| 3 1 CALLXARGS
 | |
| )A";
 | |
|   test_run_vm(fift::compile_asm(test1).move_as_ok());
 | |
| }
 | |
| 
 | |
| //TEST(VM, report3_ce) {
 | |
| //td::Slice test1 =
 | |
| //R"A(
 | |
| //s16 POP
 | |
| //s16 PUSH
 | |
| //s0 s16 XCHG
 | |
| //)A";
 | |
| //test_run_vm(fift::compile_asm(test1).move_as_ok());
 | |
| //}
 | |
| 
 | |
| TEST(VM, report3_int_overflow_1) {
 | |
|   td::Slice test1 =
 | |
|       R"A(
 | |
| 4 INT
 | |
| 16 INT
 | |
| -115792089237316195423570985008687907853269984665640564039457584007913129639936 INT
 | |
| MULDIVMOD
 | |
| )A";
 | |
|   test_run_vm(fift::compile_asm(test1).move_as_ok());
 | |
| }
 | |
| TEST(VM, report3_int_overflow_2) {
 | |
|   td::Slice test1 =
 | |
|       R"A(
 | |
| 4 INT
 | |
| 16 INT
 | |
| -115792089237316195423570985008687907853269984665640564039457584007913129639936 INT
 | |
| MULDIVR
 | |
| )A";
 | |
|   test_run_vm(fift::compile_asm(test1).move_as_ok());
 | |
| }
 | |
| 
 | |
| TEST(VM, report3_qnot) {
 | |
|   td::Slice test1 =
 | |
|       R"A(
 | |
| PUSHNAN
 | |
| QNOT
 | |
| )A";
 | |
|   test_run_vm(fift::compile_asm(test1).move_as_ok());
 | |
| }
 | |
| 
 | |
| TEST(VM, report3_loop_1) {
 | |
|   //WA
 | |
|   td::Slice test1 =
 | |
|       R"A(
 | |
| CONT:<{
 | |
|   2 INT
 | |
| }>
 | |
| ATEXITALT
 | |
| CONT:<{
 | |
|  1 INT
 | |
|  RETALT
 | |
|  -1 INT
 | |
| }>
 | |
| AGAIN
 | |
| 3 INT
 | |
| )A";
 | |
|   test_run_vm(fift::compile_asm(test1).move_as_ok());
 | |
| }
 | |
| 
 | |
| TEST(VM, report3_loop_2) {
 | |
|   //WA
 | |
|   td::Slice test1 =
 | |
|       R"A(
 | |
| CONT:<{
 | |
|   2 INT
 | |
| }>
 | |
| ATEXITALT
 | |
| CONT:<{
 | |
|  1 INT
 | |
|  RETALT
 | |
|  -1 INT
 | |
| }>
 | |
| UNTIL
 | |
| 3 INT
 | |
| )A";
 | |
|   test_run_vm(fift::compile_asm(test1).move_as_ok());
 | |
| }
 | |
| 
 | |
| TEST(VM, report3_loop_3) {
 | |
|   //WA
 | |
|   td::Slice test1 =
 | |
|       R"A(
 | |
| 1 INT
 | |
| CONT:<{
 | |
|   UNTILEND
 | |
|   RET
 | |
|   -1 INT
 | |
| }>
 | |
| CALLX
 | |
| )A";
 | |
|   test_run_vm(fift::compile_asm(test1).move_as_ok());
 | |
| }
 | |
| 
 | |
| TEST(VM, report3_loop_4) {
 | |
|   //WA
 | |
|   td::Slice test1 =
 | |
|       R"A(
 | |
| CONT:<{
 | |
|   2 INT
 | |
| }>
 | |
| ATEXITALT
 | |
| CONT:<{
 | |
|   1 INT
 | |
|   RETALT
 | |
|   -1 PUSHINT
 | |
| }>
 | |
| CONT:<{
 | |
|   -1 INT
 | |
| }>
 | |
| WHILE
 | |
| 3 INT
 | |
| )A";
 | |
|   test_run_vm(fift::compile_asm(test1).move_as_ok());
 | |
| }
 | |
| TEST(VM, report3_loop_5) {
 | |
|   //WA
 | |
|   td::Slice test1 =
 | |
|       R"A(
 | |
| CONT:<{
 | |
|   1 INT
 | |
|   2 INT
 | |
| }>
 | |
| ATEXITALT
 | |
| 3 INT
 | |
| AGAINEND
 | |
| DEC
 | |
| DUP
 | |
| IFRET
 | |
| DROP
 | |
| RETALT
 | |
| )A";
 | |
|   test_run_vm(fift::compile_asm(test1).move_as_ok());
 | |
| }
 | |
| 
 | |
| TEST(VM, report3_loop_6) {
 | |
|   //WA
 | |
|   td::Slice test1 =
 | |
|       R"A(
 | |
| CONT:<{
 | |
|   1 INT
 | |
|   2 INT
 | |
| }>
 | |
| 3 INT
 | |
| AGAINEND
 | |
| DEC
 | |
| DUP
 | |
| IFRET
 | |
| DROP
 | |
| ATEXITALT
 | |
| RETALT
 | |
| )A";
 | |
|   test_run_vm(fift::compile_asm(test1).move_as_ok());
 | |
| }
 |