From fe41b47b1c168cc27206448ddf6ae1d26d4efb3b Mon Sep 17 00:00:00 2001 From: IrKor Date: Mon, 19 Aug 2019 09:35:49 +0300 Subject: [PATCH] lite-client update --- .../lite-client/adnl/CMakeLists.txt | 2 +- .../lite-client/adnl/adnl-ext-client.cpp | 86 +- .../lite-client/adnl/adnl-ext-client.h | 2 + .../lite-client/adnl/adnl-ext-client.hpp | 50 +- .../lite-client/adnl/adnl-ext-connection.cpp | 19 +- .../lite-client/adnl/adnl-ext-connection.hpp | 1 + .../lite-client/crypto/CMakeLists.txt | 39 +- .../lite-client/crypto/block/block-parse.cpp | 2062 +++++++++ .../lite-client/crypto/block/block-parse.h | 1036 +++++ .../lite-client/crypto/block/block.cpp | 2752 ++++-------- .../lite-client/crypto/block/block.h | 1104 +---- .../lite-client/crypto/block/block.tlb | 7 +- .../lite-client/crypto/block/check-proof.cpp | 1 + .../lite-client/crypto/block/create-state.cpp | 1 + .../lite-client/crypto/block/mc-config.cpp | 187 +- .../lite-client/crypto/block/mc-config.h | 85 +- .../lite-client/crypto/block/transaction.cpp | 19 +- .../lite-client/crypto/block/transaction.h | 9 +- .../lite-client/crypto/common/bigint.hpp | 4 +- .../lite-client/crypto/common/refcnt.cpp | 37 + .../lite-client/crypto/common/refcnt.hpp | 13 +- .../lite-client/crypto/common/refint.cpp | 24 - .../lite-client/crypto/common/refint.h | 35 +- .../lite-client/crypto/func/a8.fc | 28 + .../lite-client/crypto/func/a9.fc | 23 + .../lite-client/crypto/func/a9_1.fc | 28 + .../lite-client/crypto/func/abscode.cpp | 474 ++ .../lite-client/crypto/func/analyzer.cpp | 808 ++++ .../lite-client/crypto/func/asmops.cpp | 260 ++ .../lite-client/crypto/func/b1.fc | 63 + .../lite-client/crypto/func/b2.fc | 60 + .../lite-client/crypto/func/b2_0.fc | 63 + .../lite-client/crypto/func/b2_1.fc | 62 + .../lite-client/crypto/func/b2_2.fc | 58 + .../lite-client/crypto/func/builtins.cpp | 824 ++++ .../lite-client/crypto/func/codegen.cpp | 669 +++ .../lite-client/crypto/func/func.cpp | 3921 +---------------- .../lite-client/crypto/func/func.h | 631 ++- .../lite-client/crypto/func/gen-abscode.cpp | 307 ++ .../lite-client/crypto/func/keywords.cpp | 92 + .../lite-client/crypto/func/lexer.hpp | 466 -- .../lite-client/crypto/func/optimize.cpp | 416 ++ .../lite-client/crypto/func/parse-func.cpp | 1009 +++++ .../crypto/func/stack-transform.cpp | 787 ++++ .../lite-client/crypto/func/unify-types.cpp | 276 ++ .../lite-client/crypto/parser/lexer.cpp | 267 ++ .../lite-client/crypto/parser/lexer.h | 96 + .../{func/srcread.hpp => parser/srcread.cpp} | 114 +- .../lite-client/crypto/parser/srcread.h | 132 + .../symtable.hpp => parser/symtable.cpp} | 102 +- .../lite-client/crypto/parser/symtable.h | 159 + .../lite-client/crypto/test/test-db.cpp | 13 + .../lite-client/crypto/tl/tlbc-data.h | 4 +- .../lite-client/crypto/tl/tlbc-gen-cpp.cpp | 6 +- .../lite-client/crypto/tl/tlbc.cpp | 130 +- .../lite-client/crypto/tl/tlblib.hpp | 28 +- .../lite-client/crypto/vm/boc.cpp | 4 +- .../crypto/vm/cells/CellBuilder.cpp | 18 +- .../lite-client/crypto/vm/cells/CellBuilder.h | 5 +- .../lite-client/crypto/vm/cells/CellHash.h | 2 +- .../lite-client/crypto/vm/cells/CellSlice.h | 4 +- .../lite-client/crypto/vm/continuation.h | 2 + .../lite-client/crypto/vm/dict.cpp | 169 +- .../lite-client/crypto/vm/dict.h | 39 +- .../lite-client/keys/keys.cpp | 41 + .../lite-client/keys/keys.hpp | 59 + .../lite-client/lite-client/lite-client.cpp | 47 +- .../lite-client/tdutils/CMakeLists.txt | 4 +- .../auto/extension_to_mime_type.gperf | 19 +- .../auto/mime_type_to_extension.gperf | 18 +- .../lite-client/tdutils/td/utils/DecTree.h | 3 + .../lite-client/tdutils/td/utils/Hash.h | 2 +- .../tdutils/td/utils/SharedSlice.cpp | 8 + .../tdutils/td/utils/SharedSlice.h | 23 +- .../lite-client/tdutils/td/utils/Slice-decl.h | 3 + .../lite-client/tdutils/td/utils/Slice.cpp | 25 + .../lite-client/tdutils/td/utils/Slice.h | 8 + .../lite-client/tdutils/td/utils/buffer.h | 3 + .../tdutils/td/utils/filesystem.cpp | 10 +- .../lite-client/tdutils/td/utils/filesystem.h | 2 + .../lite-client/tdutils/test/misc.cpp | 3 + .../lite-client/tl-utils/common-utils.hpp | 5 + .../lite-client/tl-utils/tl-utils.cpp | 14 +- .../lite-client/tl-utils/tl-utils.hpp | 2 + .../lite-client/tl/CMakeLists.txt | 20 +- .../tl/generate/generate_common.cpp | 2 +- .../lite-client/tl/generate/scheme/ton_api.tl | 93 +- .../tl/generate/scheme/ton_api.tlo | Bin 42852 -> 49740 bytes .../tl/generate/scheme/tonlib_api.tl | 6 +- .../tl/generate/scheme/tonlib_api.tlo | Bin 7620 -> 8420 bytes .../tl/generate/tl_writer_jni_cpp.cpp | 41 +- .../lite-client/tl/tl/tl_jni_object.cpp | 80 +- .../lite-client/tl/tl/tl_jni_object.h | 32 +- .../lite-client/ton/interfaces/block-handle.h | 83 - .../lite-client/ton/ton-shard.h | 16 +- .../lite-client/ton/ton-types.h | 19 +- .../lite-client/validator/interfaces/config.h | 25 - .../validator/interfaces/validator-full-id.h | 24 - 98 files changed, 12938 insertions(+), 7996 deletions(-) create mode 100644 ton-test-liteclient-full/lite-client/crypto/block/block-parse.cpp create mode 100644 ton-test-liteclient-full/lite-client/crypto/block/block-parse.h create mode 100644 ton-test-liteclient-full/lite-client/crypto/common/refcnt.cpp create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/a8.fc create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/a9.fc create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/a9_1.fc create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/abscode.cpp create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/analyzer.cpp create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/asmops.cpp create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/b1.fc create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/b2.fc create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/b2_0.fc create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/b2_1.fc create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/b2_2.fc create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/builtins.cpp create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/codegen.cpp create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/gen-abscode.cpp create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/keywords.cpp delete mode 100644 ton-test-liteclient-full/lite-client/crypto/func/lexer.hpp create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/optimize.cpp create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/parse-func.cpp create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/stack-transform.cpp create mode 100644 ton-test-liteclient-full/lite-client/crypto/func/unify-types.cpp create mode 100644 ton-test-liteclient-full/lite-client/crypto/parser/lexer.cpp create mode 100644 ton-test-liteclient-full/lite-client/crypto/parser/lexer.h rename ton-test-liteclient-full/lite-client/crypto/{func/srcread.hpp => parser/srcread.cpp} (56%) create mode 100644 ton-test-liteclient-full/lite-client/crypto/parser/srcread.h rename ton-test-liteclient-full/lite-client/crypto/{func/symtable.hpp => parser/symtable.cpp} (59%) create mode 100644 ton-test-liteclient-full/lite-client/crypto/parser/symtable.h create mode 100644 ton-test-liteclient-full/lite-client/tdutils/td/utils/SharedSlice.cpp create mode 100644 ton-test-liteclient-full/lite-client/tdutils/td/utils/Slice.cpp delete mode 100644 ton-test-liteclient-full/lite-client/ton/interfaces/block-handle.h delete mode 100644 ton-test-liteclient-full/lite-client/validator/interfaces/config.h delete mode 100644 ton-test-liteclient-full/lite-client/validator/interfaces/validator-full-id.h diff --git a/ton-test-liteclient-full/lite-client/adnl/CMakeLists.txt b/ton-test-liteclient-full/lite-client/adnl/CMakeLists.txt index dba2657..921914c 100644 --- a/ton-test-liteclient-full/lite-client/adnl/CMakeLists.txt +++ b/ton-test-liteclient-full/lite-client/adnl/CMakeLists.txt @@ -13,4 +13,4 @@ set (ADNL_LITE_SOURCE add_library(adnllite STATIC ${ADNL_LITE_SOURCE}) target_include_directories(adnllite PUBLIC $) -target_link_libraries(adnllite PUBLIC tdactor ton_crypto tl_api tdnet keys ) +target_link_libraries(adnllite PUBLIC tdactor ton_crypto tl_lite_api tdnet keys ) diff --git a/ton-test-liteclient-full/lite-client/adnl/adnl-ext-client.cpp b/ton-test-liteclient-full/lite-client/adnl/adnl-ext-client.cpp index 18d967a..20fa0d8 100644 --- a/ton-test-liteclient-full/lite-client/adnl/adnl-ext-client.cpp +++ b/ton-test-liteclient-full/lite-client/adnl/adnl-ext-client.cpp @@ -33,7 +33,84 @@ void AdnlExtClientImpl::alarm() { conn_ = td::actor::create_actor(td::actor::ActorOptions().with_name("outconn").with_poll(), fd.move_as_ok(), std::make_unique(actor_id(this)), dst_, - actor_id(this)); + local_id_, actor_id(this)); + } +} + +td::Status AdnlOutboundConnection::process_custom_packet(td::BufferSlice &data, bool &processed) { + if (data.size() == 12) { + auto F = fetch_tl_object(data.clone(), true); + if (F.is_ok()) { + processed = true; + return td::Status::OK(); + } + } + if (!local_id_.empty() && nonce_.size() != 0) { + auto F = fetch_tl_object(data.clone(), true); + if (F.is_ok()) { + auto f = F.move_as_ok(); + if (f->nonce_.size() == 0 || f->nonce_.size() > 512) { + return td::Status::Error(ErrorCode::protoviolation, "bad nonce size"); + } + td::SecureString ss{nonce_.size() + f->nonce_.size()}; + ss.as_mutable_slice().copy_from(nonce_.as_slice()); + ss.as_mutable_slice().remove_prefix(nonce_.size()).copy_from(f->nonce_.as_slice()); + + TRY_RESULT(dec, local_id_.create_decryptor()); + TRY_RESULT(B, dec->sign(ss.as_slice())); + + auto obj = + create_tl_object(local_id_.compute_public_key().tl(), std::move(B)); + send(serialize_tl_object(obj, true)); + + nonce_.clear(); + + processed = true; + return td::Status::OK(); + } + } + return td::Status::OK(); +} + +void AdnlOutboundConnection::start_up() { + AdnlExtConnection::start_up(); + auto X = dst_.pubkey().create_encryptor(); + if (X.is_error()) { + LOG(ERROR) << "failed to init encryptor: " << X.move_as_error(); + stop(); + return; + } + auto enc = X.move_as_ok(); + + td::BufferSlice d{256}; + auto id = dst_.compute_short_id(); + auto S = d.as_slice(); + S.copy_from(id.as_slice()); + S.remove_prefix(32); + S.truncate(256 - 64 - 32); + td::Random::secure_bytes(S); + init_crypto(S); + + auto R = enc->encrypt(S); + if (R.is_error()) { + LOG(ERROR) << "failed to encrypt: " << R.move_as_error(); + stop(); + return; + } + auto data = R.move_as_ok(); + LOG_CHECK(data.size() == 256 - 32) << "size=" << data.size(); + S = d.as_slice(); + S.remove_prefix(32); + CHECK(S.size() == data.size()); + S.copy_from(data.as_slice()); + + send_uninit(std::move(d)); + + if (!local_id_.empty()) { + nonce_ = td::SecureString{32}; + td::Random::secure_bytes(nonce_.as_mutable_slice()); + auto obj = create_tl_object(td::BufferSlice{nonce_.as_slice()}); + send(serialize_tl_object(obj, true)); } } @@ -51,6 +128,13 @@ td::actor::ActorOwn AdnlExtClient::create(AdnlNodeIdFull dst, td: return td::actor::create_actor("extclient", std::move(dst), dst_addr, std::move(callback)); } +td::actor::ActorOwn AdnlExtClient::create(AdnlNodeIdFull dst, PrivateKey local_id, + td::IPAddress dst_addr, + std::unique_ptr callback) { + return td::actor::create_actor("extclient", std::move(dst), std::move(local_id), dst_addr, + std::move(callback)); +} + td::Status AdnlOutboundConnection::process_packet(td::BufferSlice data) { TRY_RESULT(F, fetch_tl_object(std::move(data), true)); td::actor::send_closure(ext_client_, &AdnlExtClientImpl::answer_query, F->query_id_, std::move(F->answer_)); diff --git a/ton-test-liteclient-full/lite-client/adnl/adnl-ext-client.h b/ton-test-liteclient-full/lite-client/adnl/adnl-ext-client.h index b4daf41..15c312c 100644 --- a/ton-test-liteclient-full/lite-client/adnl/adnl-ext-client.h +++ b/ton-test-liteclient-full/lite-client/adnl/adnl-ext-client.h @@ -21,6 +21,8 @@ class AdnlExtClient : public td::actor::Actor { td::Promise promise) = 0; static td::actor::ActorOwn create(AdnlNodeIdFull dst, td::IPAddress dst_addr, std::unique_ptr callback); + static td::actor::ActorOwn create(AdnlNodeIdFull dst, PrivateKey local_id, td::IPAddress dst_addr, + std::unique_ptr callback); }; } // namespace adnl diff --git a/ton-test-liteclient-full/lite-client/adnl/adnl-ext-client.hpp b/ton-test-liteclient-full/lite-client/adnl/adnl-ext-client.hpp index 5de796d..9c3cc98 100644 --- a/ton-test-liteclient-full/lite-client/adnl/adnl-ext-client.hpp +++ b/ton-test-liteclient-full/lite-client/adnl/adnl-ext-client.hpp @@ -17,51 +17,28 @@ class AdnlExtClientImpl; class AdnlOutboundConnection : public AdnlExtConnection { private: AdnlNodeIdFull dst_; + PrivateKey local_id_; td::actor::ActorId ext_client_; + td::SecureString nonce_; public: AdnlOutboundConnection(td::SocketFd fd, std::unique_ptr callback, AdnlNodeIdFull dst, td::actor::ActorId ext_client) : AdnlExtConnection(std::move(fd), std::move(callback), true), dst_(std::move(dst)), ext_client_(ext_client) { } + AdnlOutboundConnection(td::SocketFd fd, std::unique_ptr callback, AdnlNodeIdFull dst, + PrivateKey local_id, td::actor::ActorId ext_client) + : AdnlExtConnection(std::move(fd), std::move(callback), true) + , dst_(std::move(dst)) + , local_id_(local_id) + , ext_client_(ext_client) { + } td::Status process_packet(td::BufferSlice data) override; td::Status process_init_packet(td::BufferSlice data) override { UNREACHABLE(); } - void start_up() override { - AdnlExtConnection::start_up(); - auto X = dst_.pubkey().create_encryptor(); - if (X.is_error()) { - LOG(ERROR) << "failed to init encryptor: " << X.move_as_error(); - stop(); - return; - } - auto enc = X.move_as_ok(); - - td::BufferSlice d{256}; - auto id = dst_.compute_short_id(); - auto S = d.as_slice(); - S.copy_from(id.as_slice()); - S.remove_prefix(32); - S.truncate(256 - 64 - 32); - td::Random::secure_bytes(S); - init_crypto(S); - - auto R = enc->encrypt(S); - if (R.is_error()) { - LOG(ERROR) << "failed to encrypt: " << R.move_as_error(); - stop(); - return; - } - auto data = R.move_as_ok(); - LOG_CHECK(data.size() == 256 - 32) << "size=" << data.size(); - S = d.as_slice(); - S.remove_prefix(32); - CHECK(S.size() == data.size()); - S.copy_from(data.as_slice()); - - send_uninit(std::move(d)); - } + td::Status process_custom_packet(td::BufferSlice &data, bool &processed) override; + void start_up() override; }; class AdnlExtClientImpl : public AdnlExtClient { @@ -69,6 +46,10 @@ class AdnlExtClientImpl : public AdnlExtClient { AdnlExtClientImpl(AdnlNodeIdFull dst_id, td::IPAddress dst_addr, std::unique_ptr callback) : dst_(std::move(dst_id)), dst_addr_(dst_addr), callback_(std::move(callback)) { } + AdnlExtClientImpl(AdnlNodeIdFull dst_id, PrivateKey local_id, td::IPAddress dst_addr, + std::unique_ptr callback) + : dst_(std::move(dst_id)), local_id_(local_id), dst_addr_(dst_addr), callback_(std::move(callback)) { + } void start_up() override { alarm_timestamp() = next_create_at_; @@ -120,6 +101,7 @@ class AdnlExtClientImpl : public AdnlExtClient { private: AdnlNodeIdFull dst_; + PrivateKey local_id_; td::IPAddress dst_addr_; std::unique_ptr callback_; diff --git a/ton-test-liteclient-full/lite-client/adnl/adnl-ext-connection.cpp b/ton-test-liteclient-full/lite-client/adnl/adnl-ext-connection.cpp index 27e9c8d..237048d 100644 --- a/ton-test-liteclient-full/lite-client/adnl/adnl-ext-connection.cpp +++ b/ton-test-liteclient-full/lite-client/adnl/adnl-ext-connection.cpp @@ -159,20 +159,11 @@ td::Status AdnlExtConnection::receive_packet(td::BufferSlice data) { // keepalive return td::Status::OK(); } - if (data.size() == 12) { - auto F = fetch_tl_object(data.clone(), true); - if (F.is_ok()) { - auto f = F.move_as_ok(); - auto obj = create_tl_object(f->random_id_); - send(serialize_tl_object(obj, true)); - return td::Status::OK(); - } - } - if (data.size() == 12) { - auto F = fetch_tl_object(data.clone(), true); - if (F.is_ok()) { - return td::Status::OK(); - } + + bool processed = false; + TRY_STATUS(process_custom_packet(data, processed)); + if (processed) { + return td::Status::OK(); } return process_packet(std::move(data)); diff --git a/ton-test-liteclient-full/lite-client/adnl/adnl-ext-connection.hpp b/ton-test-liteclient-full/lite-client/adnl/adnl-ext-connection.hpp index 0466a67..f06dad7 100644 --- a/ton-test-liteclient-full/lite-client/adnl/adnl-ext-connection.hpp +++ b/ton-test-liteclient-full/lite-client/adnl/adnl-ext-connection.hpp @@ -35,6 +35,7 @@ class AdnlExtConnection : public td::actor::Actor, public td::ObserverBase { td::Status receive(td::ChainBufferReader &input, bool &exit_loop); virtual td::Status process_packet(td::BufferSlice data) = 0; td::Status receive_packet(td::BufferSlice data); + virtual td::Status process_custom_packet(td::BufferSlice &data, bool &processed) = 0; virtual td::Status process_init_packet(td::BufferSlice data) = 0; td::Status init_crypto(td::Slice data); void stop_read() { diff --git a/ton-test-liteclient-full/lite-client/crypto/CMakeLists.txt b/ton-test-liteclient-full/lite-client/crypto/CMakeLists.txt index 77ab91f..f646592 100644 --- a/ton-test-liteclient-full/lite-client/crypto/CMakeLists.txt +++ b/ton-test-liteclient-full/lite-client/crypto/CMakeLists.txt @@ -7,6 +7,7 @@ endif() set (TON_CRYPTO_SOURCE Ed25519.cpp common/bigint.cpp + common/refcnt.cpp common/refint.cpp common/bitstring.cpp common/util.cpp @@ -143,6 +144,30 @@ set (FIFT_SOURCE fift/words.h ) +set (PARSER_SOURCE + parser/srcread.cpp + parser/lexer.cpp + parser/symtable.cpp + + parser/srcread.h + parser/lexer.h + parser/symtable.h +) + +set (FUNC_LIB_SOURCE + func/keywords.cpp + func/unify-types.cpp + func/parse-func.cpp + func/abscode.cpp + func/gen-abscode.cpp + func/analyzer.cpp + func/asmops.cpp + func/builtins.cpp + func/stack-transform.cpp + func/optimize.cpp + func/codegen.cpp +) + set(TLB_BLOCK_AUTO ${CMAKE_CURRENT_SOURCE_DIR}/block/block-auto.cpp ${CMAKE_CURRENT_SOURCE_DIR}/block/block-auto.h @@ -153,6 +178,7 @@ set (BLOCK_SOURCE block/Binlog.cpp block/block.cpp block/block-db.cpp + block/block-parse.cpp block/check-proof.cpp block/mc-config.cpp block/output-queue-merger.cpp @@ -163,6 +189,7 @@ set (BLOCK_SOURCE block/block-db-impl.h block/block-db.h block/block.h + block/block-parse.h block/check-proof.h block/output-queue-merger.h block/transaction.h @@ -222,16 +249,20 @@ if (WINGETOPT_FOUND) target_link_libraries_system(fift wingetopt) endif() -add_executable(func func/func.cpp) +add_library(src_parser ${PARSER_SOURCE}) +target_include_directories(src_parser PUBLIC $) +target_link_libraries(src_parser PUBLIC ton_crypto) + +add_executable(func func/func.cpp ${FUNC_LIB_SOURCE}) target_include_directories(func PUBLIC $) -target_link_libraries(func PUBLIC ton_crypto) +target_link_libraries(func PUBLIC ton_crypto src_parser) if (WINGETOPT_FOUND) target_link_libraries_system(func wingetopt) endif() add_executable(tlbc tl/tlbc.cpp) -target_include_directories(tlbc PUBLIC $ $) -target_link_libraries(tlbc PUBLIC ton_crypto) +target_include_directories(tlbc PUBLIC $) +target_link_libraries(tlbc PUBLIC ton_crypto src_parser) if (WINGETOPT_FOUND) target_link_libraries_system(tlbc wingetopt) endif() diff --git a/ton-test-liteclient-full/lite-client/crypto/block/block-parse.cpp b/ton-test-liteclient-full/lite-client/crypto/block/block-parse.cpp new file mode 100644 index 0000000..63a52f8 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/block/block-parse.cpp @@ -0,0 +1,2062 @@ +#include "td/utils/bits.h" +#include "block/block-parse.h" +#include "block/block-auto.h" +#include "ton/ton-shard.h" +#include "common/util.h" +#include "td/utils/crypto.h" + +namespace block { +using namespace std::literals::string_literals; + +using CombineError = vm::CombineError; + +namespace { +bool debug(const char* str) { + std::cerr << str; + return true; +} + +bool debug(int x) { + if (x < 100) { + std::cerr << '[' << (char)(64 + x) << ']'; + } else { + std::cerr << '[' << (char)(64 + x / 100) << x % 100 << ']'; + } + return true; +} +} // namespace + +#define DBG_START int dbg = 0; +#define DBG debug(++dbg)&& +#define DEB_START DBG_START +#define DEB DBG + +namespace tlb { + +using namespace ::tlb; + +int MsgAddressExt::get_size(const vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case addr_none: // 00, addr_none + return 2; + case addr_ext: // 01, addr_extern + if (cs.have(2 + 9)) { + int len = cs.prefetch_long(2 + 9) & 0x1ff; + return 2 + 9 + len; + } + } + return -1; +} + +const MsgAddressExt t_MsgAddressExt; + +const Anycast t_Anycast; + +bool Maybe_Anycast::skip_get_depth(vm::CellSlice& cs, int& depth) const { + depth = 0; + bool have; + return cs.fetch_bool_to(have) && (!have || t_Anycast.skip_get_depth(cs, depth)); +} + +const Maybe_Anycast t_Maybe_Anycast; + +bool MsgAddressInt::validate_skip(vm::CellSlice& cs) const { + if (!cs.have(3)) { + return false; + } + switch (get_tag(cs)) { + case addr_std: + return cs.advance(2) && t_Maybe_Anycast.skip(cs) && cs.advance(8 + 256); + case addr_var: + if (cs.advance(2) && t_Maybe_Anycast.skip(cs) && cs.have(9 + 32)) { + int addr_len = (int)cs.fetch_ulong(9); + int workchain_id = (int)cs.fetch_long(32); + return cs.advance(addr_len) && (workchain_id < -0x80 || workchain_id > 0x7f || addr_len != 256) && + (workchain_id != 0 && workchain_id != -1); + } + } + return false; +} + +bool MsgAddressInt::skip_get_depth(vm::CellSlice& cs, int& depth) const { + if (!cs.have(3)) { + return false; + } + switch (get_tag(cs)) { + case addr_std: + return cs.advance(2) && t_Maybe_Anycast.skip_get_depth(cs, depth) && cs.advance(8 + 256); + case addr_var: + if (cs.advance(2) && t_Maybe_Anycast.skip_get_depth(cs, depth) && cs.have(9 + 32)) { + int addr_len = (int)cs.fetch_ulong(9); + return cs.advance(32 + addr_len); + } + } + return false; +} + +ton::AccountIdPrefixFull MsgAddressInt::get_prefix(vm::CellSlice&& cs) { + if (!cs.have(3 + 8 + 64)) { + return {}; + } + ton::WorkchainId workchain; + unsigned long long prefix; + int t = (int)cs.prefetch_ulong(2 + 1 + 5); + switch (t >> 5) { + case 4: { // addr_std$10, anycast=nothing$0 + if (cs.advance(3) && cs.fetch_int_to(8, workchain) && cs.fetch_uint_to(64, prefix)) { + return {workchain, prefix}; + } + break; + } + case 5: { // addr_std$10, anycast=just$1 (Anycast) + t &= 31; // depth:(## 5) + unsigned long long rewrite; + if (cs.advance(8) && cs.fetch_uint_to(t, rewrite) // rewrite_pfx:(bits depth) + && cs.fetch_int_to(8, workchain) // workchain_id:int8 + && cs.fetch_uint_to(64, prefix)) { // address:bits256 + rewrite <<= 64 - t; + return {workchain, (prefix & (std::numeric_limits::max() >> t)) | rewrite}; + } + break; + } + case 6: { // addr_var$11, anycast=nothing$0 + int len; + if (cs.advance(3) && cs.fetch_uint_to(9, len) // addr_len:(## 9) + && len >= 64 // { len >= 64 } + && cs.fetch_int_to(32, workchain) // workchain_id:int32 + && cs.fetch_uint_to(64, prefix)) { // address:(bits addr_len) + return {workchain, prefix}; + } + break; + } + case 7: { // addr_var$11, anycast=just$1 (Anycast) + t &= 31; // depth:(## 5) + int len; + unsigned long long rewrite; + if (cs.advance(8) && cs.fetch_uint_to(t, rewrite) // rewrite_pfx:(bits depth) + && cs.fetch_uint_to(9, len) // addr_len:(## 9) + && len >= 64 // { len >= 64 } + && cs.fetch_int_to(32, workchain) // workchain_id:int32 + && cs.fetch_uint_to(64, prefix)) { // address:bits256 + rewrite <<= 64 - t; + return {workchain, (prefix & (std::numeric_limits::max() >> t)) | rewrite}; + } + break; + } + } + return {}; +} + +ton::AccountIdPrefixFull MsgAddressInt::get_prefix(const vm::CellSlice& cs) { + return get_prefix(vm::CellSlice{cs}); +} + +ton::AccountIdPrefixFull MsgAddressInt::get_prefix(Ref cs_ref) { + if (cs_ref->is_unique()) { + return get_prefix(std::move(cs_ref.unique_write())); + } else { + return get_prefix(vm::CellSlice{*cs_ref}); + } +} + +bool MsgAddressInt::extract_std_address(Ref cs_ref, ton::WorkchainId& workchain, + ton::StdSmcAddress& addr, bool rewrite) const { + if (cs_ref.is_null()) { + return false; + } else if (cs_ref->is_unique()) { + return extract_std_address(cs_ref.unique_write(), workchain, addr, rewrite); + } else { + vm::CellSlice cs{*cs_ref}; + return extract_std_address(cs, workchain, addr, rewrite); + } +} + +bool MsgAddressInt::extract_std_address(vm::CellSlice& cs, ton::WorkchainId& workchain, ton::StdSmcAddress& addr, + bool do_rewrite) const { + if (!cs.have(3 + 8 + 64)) { + return {}; + } + int t = (int)cs.prefetch_ulong(2 + 1 + 5); + switch (t >> 5) { + case 4: { // addr_std$10, anycast=nothing$0 + return cs.advance(3) && cs.fetch_int_to(8, workchain) && cs.fetch_bits_to(addr); + } + case 5: { // addr_std$10, anycast=just$1 (Anycast) + t &= 31; // depth:(## 5) + unsigned long long rewrite; + if (cs.advance(8) && cs.fetch_uint_to(t, rewrite) // rewrite_pfx:(bits depth) + && cs.fetch_int_to(8, workchain) // workchain_id:int8 + && cs.fetch_bits_to(addr)) { // address:bits256 + if (do_rewrite) { + addr.bits().store_uint(rewrite, t); + } + return true; + } + break; + } + case 6: { // addr_var$11, anycast=nothing$0 + int len; + return cs.advance(3) && cs.fetch_uint_to(9, len) // addr_len:(## 9) + && len == 256 // only 256-bit addresses are standard + && cs.fetch_int_to(32, workchain) // workchain_id:int32 + && cs.fetch_bits_to(addr); // address:(bits addr_len) + } + case 7: { // addr_var$11, anycast=just$1 (Anycast) + t &= 31; // depth:(## 5) + int len; + unsigned long long rewrite; + if (cs.advance(8) && cs.fetch_uint_to(t, rewrite) // rewrite_pfx:(bits depth) + && cs.fetch_uint_to(9, len) // addr_len:(## 9) + && len == 256 // only 256-bit addresses are standard + && cs.fetch_int_to(32, workchain) // workchain_id:int32 + && cs.fetch_bits_to(addr)) { // address:bits256 + if (do_rewrite) { + addr.bits().store_uint(rewrite, t); + } + return true; + } + break; + } + } + return false; +} + +const MsgAddressInt t_MsgAddressInt; + +bool MsgAddress::validate_skip(vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case addr_none: + case addr_ext: + return t_MsgAddressExt.validate_skip(cs); + case addr_std: + case addr_var: + return t_MsgAddressInt.validate_skip(cs); + } + return false; +} + +const MsgAddress t_MsgAddress; + +bool VarUInteger::skip(vm::CellSlice& cs) const { + int len = (int)cs.fetch_ulong(ln); + return len >= 0 && len < n && cs.advance(len * 8); +} + +bool VarUInteger::validate_skip(vm::CellSlice& cs) const { + int len = (int)cs.fetch_ulong(ln); + return len >= 0 && len < n && (!len || cs.prefetch_ulong(8)) && cs.advance(len * 8); +} + +td::RefInt256 VarUInteger::as_integer_skip(vm::CellSlice& cs) const { + int len = (int)cs.fetch_ulong(ln); + return (len >= 0 && len < n && (!len || cs.prefetch_ulong(8))) ? cs.fetch_int256(len * 8, false) : td::RefInt256{}; +} + +unsigned long long VarUInteger::as_uint(const vm::CellSlice& cs) const { + int len = (int)cs.prefetch_ulong(ln); + return len >= 0 && len <= 8 && cs.have(ln + len * 8) ? td::bitstring::bits_load_ulong(cs.data_bits() + ln, len * 8) + : std::numeric_limits::max(); +} + +bool VarUInteger::store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const { + int k = value.bit_size(false); + return k <= (n - 1) * 8 && cb.store_long_bool((k + 7) >> 3, ln) && cb.store_int256_bool(value, (k + 7) & -8, false); +} + +unsigned VarUInteger::precompute_integer_size(const td::BigInt256& value) const { + int k = value.bit_size(false); + return k <= (n - 1) * 8 ? ln + ((k + 7) & -8) : 0xfff; +} + +unsigned VarUInteger::precompute_integer_size(td::RefInt256 value) const { + if (value.is_null()) { + return 0xfff; + } + int k = value->bit_size(false); + return k <= (n - 1) * 8 ? ln + ((k + 7) & -8) : 0xfff; +} + +const VarUInteger t_VarUInteger_3{3}, t_VarUInteger_7{7}, t_VarUInteger_16{16}, t_VarUInteger_32{32}; + +bool VarUIntegerPos::skip(vm::CellSlice& cs) const { + int len = (int)cs.fetch_ulong(ln); + return len > 0 && len < n && cs.advance(len * 8); +} + +bool VarUIntegerPos::validate_skip(vm::CellSlice& cs) const { + int len = (int)cs.fetch_ulong(ln); + return len > 0 && len < n && cs.prefetch_ulong(8) && cs.advance(len * 8); +} + +td::RefInt256 VarUIntegerPos::as_integer_skip(vm::CellSlice& cs) const { + int len = (int)cs.fetch_ulong(ln); + return (len > 0 && len < n && cs.prefetch_ulong(8)) ? cs.fetch_int256(len * 8, false) : td::RefInt256{}; +} + +unsigned long long VarUIntegerPos::as_uint(const vm::CellSlice& cs) const { + int len = (int)cs.prefetch_ulong(ln); + return len > 0 && len <= 8 && cs.have(ln + len * 8) && cs.prefetch_ulong(8) + ? td::bitstring::bits_load_ulong(cs.data_bits() + ln, len * 8) + : std::numeric_limits::max(); +} + +bool VarUIntegerPos::store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const { + int k = value.bit_size(false); + return k <= (n - 1) * 8 && value.sgn() > 0 && cb.store_long_bool((k + 7) >> 3, ln) && + cb.store_int256_bool(value, (k + 7) & -8, false); +} + +const VarUIntegerPos t_VarUIntegerPos_16{16}, t_VarUIntegerPos_32{32}; + +static inline bool redundant_int(const vm::CellSlice& cs) { + int t = (int)cs.prefetch_long(9); + return t == 0 || t == -1; +} + +bool VarInteger::skip(vm::CellSlice& cs) const { + int len = (int)cs.fetch_ulong(ln); + return len >= 0 && len < n && cs.advance(len * 8); +} + +bool VarInteger::validate_skip(vm::CellSlice& cs) const { + int len = (int)cs.fetch_ulong(ln); + return len >= 0 && len < n && (!len || !redundant_int(cs)) && cs.advance(len * 8); +} + +td::RefInt256 VarInteger::as_integer_skip(vm::CellSlice& cs) const { + int len = (int)cs.fetch_ulong(ln); + return (len >= 0 && len < n && (!len || !redundant_int(cs))) ? cs.fetch_int256(len * 8, true) : td::RefInt256{}; +} + +long long VarInteger::as_int(const vm::CellSlice& cs) const { + int len = (int)cs.prefetch_ulong(ln); + return len >= 0 && len <= 8 && cs.have(ln + len * 8) ? td::bitstring::bits_load_long(cs.data_bits() + ln, len * 8) + : (1ULL << 63); +} + +bool VarInteger::store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const { + int k = value.bit_size(true); + return k <= (n - 1) * 8 && cb.store_long_bool((k + 7) >> 3, ln) && cb.store_int256_bool(value, (k + 7) & -8, true); +} + +bool VarIntegerNz::skip(vm::CellSlice& cs) const { + int len = (int)cs.fetch_ulong(ln); + return len > 0 && len < n && cs.advance(len * 8); +} + +bool VarIntegerNz::validate_skip(vm::CellSlice& cs) const { + int len = (int)cs.fetch_ulong(ln); + return len > 0 && len < n && !redundant_int(cs) && cs.advance(len * 8); +} + +td::RefInt256 VarIntegerNz::as_integer_skip(vm::CellSlice& cs) const { + int len = (int)cs.fetch_ulong(ln); + return (len > 0 && len < n && !redundant_int(cs)) ? cs.fetch_int256(len * 8, true) : td::RefInt256{}; +} + +long long VarIntegerNz::as_int(const vm::CellSlice& cs) const { + int len = (int)cs.prefetch_ulong(ln); + return len > 0 && len <= 8 && cs.have(ln + len * 8) && !redundant_int(cs) + ? td::bitstring::bits_load_long(cs.data_bits() + ln, len * 8) + : (1ULL << 63); +} + +bool VarIntegerNz::store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const { + int k = value.bit_size(true); + return k <= (n - 1) * 8 && value.sgn() != 0 && cb.store_long_bool((k + 7) >> 3, ln) && + cb.store_int256_bool(value, (k + 7) & -8, true); +} + +bool Grams::validate_skip(vm::CellSlice& cs) const { + return t_VarUInteger_16.validate_skip(cs); +} + +td::RefInt256 Grams::as_integer_skip(vm::CellSlice& cs) const { + return t_VarUInteger_16.as_integer_skip(cs); +} + +bool Grams::null_value(vm::CellBuilder& cb) const { + return t_VarUInteger_16.null_value(cb); +} + +bool Grams::store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const { + return t_VarUInteger_16.store_integer_value(cb, value); +} + +unsigned Grams::precompute_size(const td::BigInt256& value) const { + return t_VarUInteger_16.precompute_integer_size(value); +} + +unsigned Grams::precompute_size(td::RefInt256 value) const { + return t_VarUInteger_16.precompute_integer_size(std::move(value)); +} + +const Grams t_Grams; + +const Unary t_Unary; + +bool HmLabel::validate_skip(vm::CellSlice& cs, int& n) const { + switch (get_tag(cs)) { + case hml_short: + return cs.advance(1) && (n = cs.count_leading(1)) <= m && cs.advance(2 * n + 1); + case hml_long: + return cs.advance(2) && cs.fetch_uint_leq(m, n) && cs.advance(n); + case hml_same: + return cs.advance(3) && cs.fetch_uint_leq(m, n); + } + return false; +} + +int HmLabel::get_tag(const vm::CellSlice& cs) const { + int tag = (int)cs.prefetch_ulong(2); + return tag != 1 ? tag : hml_short; +} + +int HashmapNode::get_size(const vm::CellSlice& cs) const { + assert(n >= 0); + return n ? 0x20000 : value_type.get_size(cs); +} + +bool HashmapNode::skip(vm::CellSlice& cs) const { + assert(n >= 0); + return n ? cs.advance_refs(2) : value_type.skip(cs); +} + +bool HashmapNode::validate_skip(vm::CellSlice& cs) const { + assert(n >= 0); + if (!n) { + // hmn_leaf + return value_type.validate_skip(cs); + } else { + // hmn_fork + Hashmap branch_type{n - 1, value_type}; + return branch_type.validate_ref(cs.fetch_ref()) && branch_type.validate_ref(cs.fetch_ref()); + } +} + +bool Hashmap::skip(vm::CellSlice& cs) const { + int l; + return HmLabel{n}.skip(cs, l) && HashmapNode{n - l, value_type}.skip(cs); +} + +bool Hashmap::validate_skip(vm::CellSlice& cs) const { + int l; + return HmLabel{n}.skip(cs, l) && HashmapNode{n - l, value_type}.skip(cs); +} + +int HashmapE::get_size(const vm::CellSlice& cs) const { + int tag = get_tag(cs); + return (tag >= 0 ? (tag > 0 ? 0x10001 : 1) : -1); +} + +bool HashmapE::validate(const vm::CellSlice& cs) const { + int tag = get_tag(cs); + return tag <= 0 ? !tag : root_type.validate_ref(cs.prefetch_ref()); +} + +bool HashmapE::add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const { + int n = root_type.n; + vm::Dictionary dict1{vm::DictAdvance(), cs1, n}, dict2{vm::DictAdvance(), cs2, n}; + const TLB& vt = root_type.value_type; + vm::Dictionary::simple_combine_func_t combine = [vt](vm::CellBuilder& cb, Ref cs1_ref, + Ref cs2_ref) -> bool { + if (!vt.add_values(cb, cs1_ref.write(), cs2_ref.write())) { + throw CombineError{}; + } + return true; + }; + return dict1.combine_with(dict2, combine) && std::move(dict1).append_dict_to_bool(cb); +} + +bool HashmapE::add_values_ref(Ref& res, Ref arg1, Ref arg2) const { + int n = root_type.n; + vm::Dictionary dict1{std::move(arg1), n}, dict2{std::move(arg2), n}; + const TLB& vt = root_type.value_type; + vm::Dictionary::simple_combine_func_t combine = [vt](vm::CellBuilder& cb, Ref cs1_ref, + Ref cs2_ref) -> bool { + if (!vt.add_values(cb, cs1_ref.write(), cs2_ref.write())) { + throw CombineError{}; + } + return true; + }; + if (dict1.combine_with(dict2, combine)) { + dict2.reset(); + res = std::move(dict1).extract_root_cell(); + return true; + } else { + res = Ref{}; + return false; + } +} + +int HashmapE::sub_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const { + int n = root_type.n; + vm::Dictionary dict1{vm::DictAdvance(), cs1, n}, dict2{vm::DictAdvance(), cs2, n}; + const TLB& vt = root_type.value_type; + vm::Dictionary::simple_combine_func_t combine = [vt](vm::CellBuilder& cb, Ref cs1_ref, + Ref cs2_ref) -> bool { + int r = vt.sub_values(cb, cs1_ref.write(), cs2_ref.write()); + if (r < 0) { + throw CombineError{}; + } + return r; + }; + if (!dict1.combine_with(dict2, combine, 1)) { + return -1; + } + dict2.reset(); + bool not_empty = !dict1.is_empty(); + return std::move(dict1).append_dict_to_bool(cb) ? not_empty : -1; +} + +int HashmapE::sub_values_ref(Ref& res, Ref arg1, Ref arg2) const { + int n = root_type.n; + vm::Dictionary dict1{std::move(arg1), n}, dict2{std::move(arg2), n}; + const TLB& vt = root_type.value_type; + vm::Dictionary::simple_combine_func_t combine = [vt](vm::CellBuilder& cb, Ref cs1_ref, + Ref cs2_ref) -> bool { + int r = vt.sub_values(cb, cs1_ref.write(), cs2_ref.write()); + if (r < 0) { + throw CombineError{}; + } + return r; + }; + if (dict1.combine_with(dict2, combine, 1)) { + dict2.reset(); + res = std::move(dict1).extract_root_cell(); + return res.not_null(); + } else { + res = Ref{}; + return -1; + } +} + +bool HashmapE::store_ref(vm::CellBuilder& cb, Ref arg) const { + if (arg.is_null()) { + return cb.store_long_bool(0, 1); + } else { + return cb.store_long_bool(1, 1) && cb.store_ref_bool(std::move(arg)); + } +} + +const ExtraCurrencyCollection t_ExtraCurrencyCollection; + +bool CurrencyCollection::validate_skip(vm::CellSlice& cs) const { + return t_Grams.validate_skip(cs) && t_ExtraCurrencyCollection.validate_skip(cs); +} + +bool CurrencyCollection::skip(vm::CellSlice& cs) const { + return t_Grams.skip(cs) && t_ExtraCurrencyCollection.skip(cs); +} + +td::RefInt256 CurrencyCollection::as_integer_skip(vm::CellSlice& cs) const { + auto res = t_Grams.as_integer_skip(cs); + if (res.not_null() && t_ExtraCurrencyCollection.skip(cs)) { + return res; + } else { + return {}; + } +} + +bool CurrencyCollection::add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const { + return t_Grams.add_values(cb, cs1, cs2) && t_ExtraCurrencyCollection.add_values(cb, cs1, cs2); +} + +bool CurrencyCollection::unpack_special(vm::CellSlice& cs, td::RefInt256& balance, Ref& extra) const { + balance = t_Grams.as_integer_skip(cs); + if (cs.fetch_ulong(1) == 1) { + return balance.not_null() && cs.fetch_ref_to(extra) && cs.empty_ext(); + } else { + extra.clear(); + return balance.not_null() && cs.empty_ext(); + } +} + +bool CurrencyCollection::pack_special(vm::CellBuilder& cb, td::RefInt256 balance, Ref extra) const { + return t_Grams.store_integer_ref(cb, std::move(balance)) && t_ExtraCurrencyCollection.store_ref(cb, std::move(extra)); +} + +bool CurrencyCollection::unpack(vm::CellSlice& cs, block::CurrencyCollection& res) const { + return unpack_special(cs, res.grams, res.extra); +} + +bool CurrencyCollection::pack(vm::CellBuilder& cb, const block::CurrencyCollection& res) const { + return res.is_valid() && pack_special(cb, res.grams, res.extra); +} + +const CurrencyCollection t_CurrencyCollection; + +bool CommonMsgInfo::validate_skip(vm::CellSlice& cs) const { + int tag = get_tag(cs); + switch (tag) { + case int_msg_info: + return cs.advance(4) // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool + && t_MsgAddressInt.validate_skip(cs) // src + && t_MsgAddressInt.validate_skip(cs) // dest + && t_CurrencyCollection.validate_skip(cs) // value + && t_Grams.validate_skip(cs) // ihr_fee + && t_Grams.validate_skip(cs) // fwd_fee + && cs.advance(64 + 32); // created_lt:uint64 created_at:uint32 + case ext_in_msg_info: + return cs.advance(2) && t_MsgAddressExt.validate_skip(cs) // src + && t_MsgAddressInt.validate_skip(cs) // dest + && t_Grams.validate_skip(cs); // import_fee + case ext_out_msg_info: + return cs.advance(2) && t_MsgAddressInt.validate_skip(cs) // src + && t_MsgAddressExt.validate_skip(cs) // dest + && cs.advance(64 + 32); // created_lt:uint64 created_at:uint32 + } + return false; +} + +bool CommonMsgInfo::unpack(vm::CellSlice& cs, CommonMsgInfo::Record_int_msg_info& data) const { + return get_tag(cs) == int_msg_info && cs.advance(1) && cs.fetch_bool_to(data.ihr_disabled) && + cs.fetch_bool_to(data.bounce) && cs.fetch_bool_to(data.bounced) && t_MsgAddressInt.fetch_to(cs, data.src) && + t_MsgAddressInt.fetch_to(cs, data.dest) && t_CurrencyCollection.fetch_to(cs, data.value) && + t_Grams.fetch_to(cs, data.ihr_fee) && t_Grams.fetch_to(cs, data.fwd_fee) && + cs.fetch_uint_to(64, data.created_lt) && cs.fetch_uint_to(32, data.created_at); +} + +bool CommonMsgInfo::skip(vm::CellSlice& cs) const { + int tag = get_tag(cs); + switch (tag) { + case int_msg_info: + return cs.advance(4) // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool + && t_MsgAddressInt.skip(cs) // src + && t_MsgAddressInt.skip(cs) // dest + && t_CurrencyCollection.skip(cs) // value + && t_Grams.skip(cs) // ihr_fee + && t_Grams.skip(cs) // fwd_fee + && cs.advance(64 + 32); // created_lt:uint64 created_at:uint32 + case ext_in_msg_info: + return cs.advance(2) && t_MsgAddressExt.skip(cs) // src + && t_MsgAddressInt.skip(cs) // dest + && t_Grams.skip(cs); // import_fee + case ext_out_msg_info: + return cs.advance(2) && t_MsgAddressInt.skip(cs) // src + && t_MsgAddressExt.skip(cs) // dest + && cs.advance(64 + 32); // created_lt:uint64 created_at:uint32 + } + return false; +} + +bool CommonMsgInfo::get_created_lt(vm::CellSlice& cs, unsigned long long& created_lt) const { + switch (get_tag(cs)) { + case int_msg_info: + return cs.advance(4) // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool + && t_MsgAddressInt.skip(cs) // src + && t_MsgAddressInt.skip(cs) // dest + && t_CurrencyCollection.skip(cs) // value + && t_Grams.skip(cs) // ihr_fee + && t_Grams.skip(cs) // fwd_fee + && cs.fetch_ulong_bool(64, created_lt) // created_lt:uint64 + && cs.advance(32); // created_at:uint32 + case ext_in_msg_info: + return false; + case ext_out_msg_info: + return cs.advance(2) && t_MsgAddressInt.skip(cs) // src + && t_MsgAddressExt.skip(cs) // dest + && cs.fetch_ulong_bool(64, created_lt) // created_lt:uint64 + && cs.advance(32); // created_at:uint32 + } + return false; +} + +const CommonMsgInfo t_CommonMsgInfo; +const TickTock t_TickTock; +const RefAnything t_RefCell; + +bool StateInit::validate_skip(vm::CellSlice& cs) const { + return Maybe{5}.validate_skip(cs) // split_depth:(Maybe (## 5)) + && Maybe{}.validate_skip(cs) // special:(Maybe TickTock) + && Maybe{}.validate_skip(cs) // code:(Maybe ^Cell) + && Maybe{}.validate_skip(cs) // data:(Maybe ^Cell) + && Maybe{}.validate_skip(cs); // library:(Maybe ^Cell) +} + +bool StateInit::get_ticktock(vm::CellSlice& cs, int& ticktock) const { + bool have_tt; + ticktock = 0; + return Maybe{5}.validate_skip(cs) && cs.fetch_bool_to(have_tt) && (!have_tt || cs.fetch_uint_to(2, ticktock)); +} + +const StateInit t_StateInit; + +bool Message::validate_skip(vm::CellSlice& cs) const { + static const Maybe>> init_type; + static const Either body_type; + return t_CommonMsgInfo.validate_skip(cs) // info:CommonMsgInfo + && init_type.validate_skip(cs) // init:(Maybe (Either StateInit ^StateInit)) + && body_type.validate_skip(cs); // body:(Either X ^X) +} + +bool Message::extract_info(vm::CellSlice& cs) const { + return t_CommonMsgInfo.extract(cs); +} + +bool Message::get_created_lt(vm::CellSlice& cs, unsigned long long& created_lt) const { + return t_CommonMsgInfo.get_created_lt(cs, created_lt); +} + +bool Message::is_internal(Ref ref) const { + return is_internal(load_cell_slice(std::move(ref))); +} + +const Message t_Message; +const RefTo t_Ref_Message; + +bool IntermediateAddress::validate_skip(vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case interm_addr_regular: + return cs.advance(1) && cs.fetch_ulong(7) <= 96U; + case interm_addr_simple: + return cs.advance(2 + 8 + 64); + case interm_addr_ext: + if (cs.have(2 + 32 + 64)) { + cs.advance(2); + int workchain_id = (int)cs.fetch_long(32); + return (workchain_id < -128 || workchain_id >= 128) && cs.advance(64); + } + // no break + } + return false; +} + +bool IntermediateAddress::skip(vm::CellSlice& cs) const { + return cs.advance(get_size(cs)); +} + +int IntermediateAddress::get_size(const vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case interm_addr_regular: + return 1 + 7; + case interm_addr_simple: + return 2 + 8 + 64; + case interm_addr_ext: + return 2 + 32 + 64; + } + return -1; +} + +const IntermediateAddress t_IntermediateAddress; + +bool MsgEnvelope::validate_skip(vm::CellSlice& cs) const { + return cs.fetch_ulong(4) == 4 // msg_envelope#4 + && t_IntermediateAddress.validate_skip(cs) // cur_addr:IntermediateAddress + && t_IntermediateAddress.validate_skip(cs) // next_addr:IntermediateAddress + && t_Grams.validate_skip(cs) // fwd_fee_remaining:Grams + && t_Ref_Message.validate_skip(cs); // msg:^Message +} + +bool MsgEnvelope::skip(vm::CellSlice& cs) const { + return cs.advance(4) // msg_envelope#4 + && t_IntermediateAddress.skip(cs) // cur_addr:IntermediateAddress + && t_IntermediateAddress.skip(cs) // next_addr:IntermediateAddress + && t_Grams.skip(cs) // fwd_fee_remaining:Grams + && t_Ref_Message.skip(cs); // msg:^Message +} + +bool MsgEnvelope::extract_fwd_fees_remaining(vm::CellSlice& cs) const { + return t_IntermediateAddress.skip(cs) && t_IntermediateAddress.skip(cs) && t_Grams.extract(cs); +} + +bool MsgEnvelope::unpack(vm::CellSlice& cs, MsgEnvelope::Record& data) const { + return cs.fetch_ulong(4) == 4 // msg_envelope#4 + && t_IntermediateAddress.fetch_to(cs, data.cur_addr) // cur_addr:IntermediateAddress + && t_IntermediateAddress.fetch_to(cs, data.next_addr) // next_addr:IntermediateAddress + && t_Grams.fetch_to(cs, data.fwd_fee_remaining) // fwd_fee_remaining:Grams + && cs.fetch_ref_to(data.msg); // msg:^Message +} + +bool MsgEnvelope::unpack(vm::CellSlice& cs, MsgEnvelope::Record_std& data) const { + return cs.fetch_ulong(4) == 4 // msg_envelope#4 + && t_IntermediateAddress.fetch_regular(cs, data.cur_addr) // cur_addr:IntermediateAddress + && t_IntermediateAddress.fetch_regular(cs, data.next_addr) // next_addr:IntermediateAddress + && t_Grams.as_integer_skip_to(cs, data.fwd_fee_remaining) // fwd_fee_remaining:Grams + && cs.fetch_ref_to(data.msg); // msg:^Message +} + +bool MsgEnvelope::unpack_std(vm::CellSlice& cs, int& cur_a, int& nhop_a, Ref& msg) const { + return cs.fetch_ulong(4) == 4 // msg_envelope#4 + && t_IntermediateAddress.fetch_regular(cs, cur_a) // cur_addr:IntermediateAddress + && t_IntermediateAddress.fetch_regular(cs, nhop_a) // next_addr:IntermediateAddress + && cs.fetch_ref_to(msg); +} + +bool MsgEnvelope::get_created_lt(const vm::CellSlice& cs, unsigned long long& created_lt) const { + if (!cs.size_refs()) { + return false; + } + auto msg_cs = load_cell_slice(cs.prefetch_ref()); + return t_Message.get_created_lt(msg_cs, created_lt); +} + +const MsgEnvelope t_MsgEnvelope; +const RefTo t_Ref_MsgEnvelope; + +bool StorageUsed::validate_skip(vm::CellSlice& cs) const { + return t_VarUInteger_7.validate_skip(cs) // cells:(VarUInteger 7) + && t_VarUInteger_7.validate_skip(cs) // bits:(VarUInteger 7) + && t_VarUInteger_7.validate_skip(cs); // public_cells:(VarUInteger 7) +} + +bool StorageUsed::skip(vm::CellSlice& cs) const { + return t_VarUInteger_7.skip(cs) // cells:(VarUInteger 7) + && t_VarUInteger_7.skip(cs) // bits:(VarUInteger 7) + && t_VarUInteger_7.skip(cs); // public_cells:(VarUInteger 7) +} + +const StorageUsed t_StorageUsed; + +bool StorageUsedShort::validate_skip(vm::CellSlice& cs) const { + return t_VarUInteger_7.validate_skip(cs) // cells:(VarUInteger 7) + && t_VarUInteger_7.validate_skip(cs); // bits:(VarUInteger 7) +} + +bool StorageUsedShort::skip(vm::CellSlice& cs) const { + return t_VarUInteger_7.skip(cs) // cells:(VarUInteger 7) + && t_VarUInteger_7.skip(cs); // bits:(VarUInteger 7) +} + +const StorageUsedShort t_StorageUsedShort; + +const Maybe t_Maybe_Grams; + +bool StorageInfo::skip(vm::CellSlice& cs) const { + return t_StorageUsed.skip(cs) // used:StorageUsed + && cs.advance(32) // last_paid:uint32 + && t_Maybe_Grams.skip(cs); // due_payment:(Maybe Grams) +} + +bool StorageInfo::validate_skip(vm::CellSlice& cs) const { + return t_StorageUsed.validate_skip(cs) // used:StorageUsed + && cs.advance(32) // last_paid:uint32 + && t_Maybe_Grams.validate_skip(cs); // due_payment:(Maybe Grams) +} + +const StorageInfo t_StorageInfo; + +bool AccountState::validate_skip(vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case account_uninit: + return cs.advance(2); + case account_frozen: + return cs.advance(2 + 256); + case account_active: + return cs.advance(1) && t_StateInit.validate_skip(cs); + } + return false; +} + +bool AccountState::get_ticktock(vm::CellSlice& cs, int& ticktock) const { + if (get_tag(cs) != account_active) { + ticktock = 0; + return true; + } + return cs.advance(1) && t_StateInit.get_ticktock(cs, ticktock); +} + +const AccountState t_AccountState; + +bool AccountStorage::skip(vm::CellSlice& cs) const { + return cs.advance(64) && t_CurrencyCollection.skip(cs) && t_AccountState.skip(cs); +} + +bool AccountStorage::skip_copy_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const { + return cs.advance(64) && t_CurrencyCollection.skip_copy(cb, cs) && t_AccountState.skip(cs); +} + +bool AccountStorage::validate_skip(vm::CellSlice& cs) const { + return cs.advance(64) && t_CurrencyCollection.validate_skip(cs) && t_AccountState.validate_skip(cs); +} + +const AccountStorage t_AccountStorage; + +bool Account::skip(vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case account_none: + return cs.advance(1); + case account: + return cs.advance(1) // account$1 + && t_MsgAddressInt.skip(cs) // addr:MsgAddressInt + && t_StorageInfo.skip(cs) // storage_stat:StorageInfo + && t_AccountStorage.skip(cs); // storage:AccountStorage + } + return false; +} + +bool Account::validate_skip(vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case account_none: + return allow_empty && cs.advance(1); + case account: + return cs.advance(1) // account$1 + && t_MsgAddressInt.validate_skip(cs) // addr:MsgAddressInt + && t_StorageInfo.validate_skip(cs) // storage_stat:StorageInfo + && t_AccountStorage.validate_skip(cs); // storage:AccountStorage + } + return false; +} + +bool Account::skip_copy_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case account_none: + return allow_empty && cs.advance(1) && t_CurrencyCollection.null_value(cb); + case account: + return cs.advance(1) // account$1 + && t_MsgAddressInt.skip(cs) // addr:MsgAddressInt + && t_StorageInfo.skip(cs) // storage_stat:StorageInfo + && t_AccountStorage.skip_copy_balance(cb, cs); // storage:AccountStorage + } + return false; +} + +bool Account::skip_copy_depth_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const { + int depth; + switch (get_tag(cs)) { + case account_none: + return allow_empty && cs.advance(1) && t_DepthBalanceInfo.null_value(cb); + case account: + return cs.advance(1) // account$1 + && t_MsgAddressInt.skip_get_depth(cs, depth) // addr:MsgAddressInt + && cb.store_uint_leq(30, depth) // -> store split_depth:(#<= 30) + && t_StorageInfo.skip(cs) // storage_stat:StorageInfo + && t_AccountStorage.skip_copy_balance(cb, cs); // storage:AccountStorage + } + return false; +} + +const Account t_Account, t_AccountE{true}; +const RefTo t_Ref_Account; + +bool ShardAccount::extract_account_state(Ref cs_ref, Ref& acc_state) { + if (cs_ref.is_null()) { + vm::CellBuilder cb; + return cb.store_bool_bool(false) && cb.finalize_to(acc_state); + } else { + return cs_ref->prefetch_ref_to(acc_state); + } +} + +bool ShardAccount::Record::reset() { + last_trans_hash.set_zero(); + last_trans_lt = 0; + is_zero = valid = true; + vm::CellBuilder cb; + return (cb.store_bool_bool(false) && cb.finalize_to(account)) || invalidate(); +} + +bool ShardAccount::Record::unpack(vm::CellSlice& cs) { + is_zero = false; + valid = true; + return (cs.fetch_ref_to(account) && cs.fetch_bits_to(last_trans_hash) && cs.fetch_uint_to(64, last_trans_lt)) || + invalidate(); +} + +bool ShardAccount::Record::unpack(Ref cs_ref) { + if (cs_ref.not_null()) { + return unpack(cs_ref.write()) && (cs_ref->empty_ext() || invalidate()); + } else { + return reset(); + } +} + +const ShardAccount t_ShardAccount; + +const AccountStatus t_AccountStatus; + +bool HashmapAugNode::skip(vm::CellSlice& cs) const { + if (n < 0) { + return false; + } else if (!n) { + // ahmn_leaf + return aug.extra_type.skip(cs) && aug.value_type.skip(cs); + } else { + // ahmn_fork + return cs.advance_refs(2) && aug.extra_type.skip(cs); + } +} + +bool HashmapAugNode::validate_skip(vm::CellSlice& cs) const { + if (n < 0) { + return false; + } + if (!n) { + // ahmn_leaf + vm::CellSlice cs_extra{cs}; + if (!aug.extra_type.validate_skip(cs)) { + return false; + } + cs_extra.cut_tail(cs); + vm::CellSlice cs_value{cs}; + if (!aug.value_type.validate_skip(cs)) { + return false; + } + cs_value.cut_tail(cs); + return aug.check_leaf(cs_extra, cs_value); + } + // ahmn_fork + if (!cs.have_refs(2)) { + return false; + } + HashmapAug branch_type{n - 1, aug}; + if (!branch_type.validate_ref(cs.prefetch_ref(0)) || !branch_type.validate_ref(cs.prefetch_ref(1))) { + return false; + } + auto cs_left = load_cell_slice(cs.fetch_ref()); + auto cs_right = load_cell_slice(cs.fetch_ref()); + vm::CellSlice cs_extra{cs}; + if (!aug.extra_type.validate_skip(cs)) { + return false; + } + cs_extra.cut_tail(cs); + return branch_type.extract_extra(cs_left) && branch_type.extract_extra(cs_right) && + aug.check_fork(cs_extra, cs_left, cs_right); +} + +bool HashmapAug::skip(vm::CellSlice& cs) const { + int l; + return HmLabel{n}.skip(cs, l) && HashmapAugNode{n - l, aug}.skip(cs); +} + +bool HashmapAug::validate_skip(vm::CellSlice& cs) const { + int l; + return HmLabel{n}.validate_skip(cs, l) && HashmapAugNode{n - l, aug}.validate_skip(cs); +} + +bool HashmapAug::extract_extra(vm::CellSlice& cs) const { + int l; + return HmLabel{n}.skip(cs, l) && (l == n || cs.advance_refs(2)) && aug.extra_type.extract(cs); +} + +bool HashmapAugE::validate_skip(vm::CellSlice& cs) const { + Ref extra; + switch (get_tag(cs)) { + case ahme_empty: + return cs.advance(1) && (extra = root_type.aug.extra_type.validate_fetch(cs)).not_null() && + root_type.aug.check_empty(extra.unique_write()); + case ahme_root: + if (cs.advance(1) && root_type.validate_ref(cs.prefetch_ref())) { + auto cs_root = load_cell_slice(cs.fetch_ref()); + return (extra = root_type.aug.extra_type.validate_fetch(cs)).not_null() && root_type.extract_extra(cs_root) && + extra->contents_equal(cs_root); + } + break; + } + return false; +} + +bool HashmapAugE::skip(vm::CellSlice& cs) const { + int tag = (int)cs.fetch_ulong(1); + return tag >= 0 && cs.advance_refs(tag) && root_type.aug.extra_type.skip(cs); +} + +bool HashmapAugE::extract_extra(vm::CellSlice& cs) const { + int tag = (int)cs.fetch_ulong(1); + return tag >= 0 && cs.advance_refs(tag) && root_type.aug.extra_type.extract(cs); +} + +bool DepthBalanceInfo::skip(vm::CellSlice& cs) const { + return cs.advance(5) && + t_CurrencyCollection.skip( + cs); // depth_balance$_ split_depth:(#<= 30) balance:CurrencyCollection = DepthBalanceInfo; +} + +bool DepthBalanceInfo::validate_skip(vm::CellSlice& cs) const { + return cs.fetch_ulong(5) <= 30 && + t_CurrencyCollection.validate_skip(cs); // depth_balance$_ split_depth:(#<= 30) balance:CurrencyCollection +} + +bool DepthBalanceInfo::null_value(vm::CellBuilder& cb) const { + return cb.store_zeroes_bool(5) && t_CurrencyCollection.null_value(cb); +} + +bool DepthBalanceInfo::add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const { + unsigned d1, d2; + return cs1.fetch_uint_leq(30, d1) && cs2.fetch_uint_leq(30, d2) && cb.store_uint_leq(30, std::max(d1, d2)) && + t_CurrencyCollection.add_values(cb, cs1, cs2); +} + +const DepthBalanceInfo t_DepthBalanceInfo; + +bool Aug_ShardAccounts::eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const { + if (cs.have_refs()) { + auto cs2 = load_cell_slice(cs.prefetch_ref()); + return t_Account.skip_copy_depth_balance(cb, cs2); + } else { + return false; + } +} + +const Aug_ShardAccounts aug_ShardAccounts; + +const ShardAccounts t_ShardAccounts; + +const AccStatusChange t_AccStatusChange; + +bool TrStoragePhase::skip(vm::CellSlice& cs) const { + return t_Grams.skip(cs) // storage_fees_collected:Grams + && t_Maybe_Grams.skip(cs) // storage_fees_due:Grams + && t_AccStatusChange.skip(cs); // status_change:AccStatusChange +} + +bool TrStoragePhase::validate_skip(vm::CellSlice& cs) const { + return t_Grams.validate_skip(cs) // storage_fees_collected:Grams + && t_Maybe_Grams.validate_skip(cs) // storage_fees_due:Grams + && t_AccStatusChange.validate_skip(cs); // status_change:AccStatusChange +} + +const TrStoragePhase t_TrStoragePhase; + +bool TrCreditPhase::skip(vm::CellSlice& cs) const { + return t_Maybe_Grams.skip(cs) // due_fees_collected:(Maybe Grams) + && t_CurrencyCollection.skip(cs); // credit:CurrencyCollection +} + +bool TrCreditPhase::validate_skip(vm::CellSlice& cs) const { + return t_Maybe_Grams.validate_skip(cs) // due_fees_collected:(Maybe Grams) + && t_CurrencyCollection.validate_skip(cs); // credit:CurrencyCollection +} + +const TrCreditPhase t_TrCreditPhase; + +bool TrComputeInternal1::skip(vm::CellSlice& cs) const { + return t_VarUInteger_7.skip(cs) // gas_used:(VarUInteger 7) + && t_VarUInteger_7.skip(cs) // gas_limit:(VarUInteger 7) + && Maybe{3}.skip(cs) // gas_credit:(Maybe (VarUInteger 3)) + && cs.advance(8 + 32) // mode:int8 exit_code:int32 + && Maybe{32}.skip(cs) // exit_arg:(Maybe int32) + && cs.advance(32 + 256 + 256); // vm_steps:uint32 + // vm_init_state_hash:uint256 + // vm_final_state_hash:uint256 +} + +bool TrComputeInternal1::validate_skip(vm::CellSlice& cs) const { + return t_VarUInteger_7.validate_skip(cs) // gas_used:(VarUInteger 7) + && t_VarUInteger_7.validate_skip(cs) // gas_limit:(VarUInteger 7) + && Maybe{3}.validate_skip(cs) // gas_credit:(Maybe (VarUInteger 3)) + && cs.advance(8 + 32) // mode:int8 exit_code:int32 + && Maybe{32}.validate_skip(cs) // exit_arg:(Maybe int32) + && cs.advance(32 + 256 + 256); // vm_steps:uint32 + // vm_init_state_hash:uint256 + // vm_final_state_hash:uint256 +} + +const TrComputeInternal1 t_TrComputeInternal1; +const RefTo t_Ref_TrComputeInternal1; +const ComputeSkipReason t_ComputeSkipReason; + +bool TrComputePhase::skip(vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case tr_phase_compute_skipped: + return cs.advance(1) && t_ComputeSkipReason.skip(cs); + case tr_phase_compute_vm: + return cs.advance(1 + 3) // tr_phase_compute_vm$1 success:Bool msg_state_used:Bool account_activated:Bool + && t_Grams.skip(cs) // gas_fees:Grams + && t_Ref_TrComputeInternal1.skip(cs); // ^[ gas_used:(..) .. ] + } + return false; +} + +bool TrComputePhase::validate_skip(vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case tr_phase_compute_skipped: + return cs.advance(1) && t_ComputeSkipReason.validate_skip(cs); + case tr_phase_compute_vm: + return cs.advance(1 + 3) // tr_phase_compute_vm$1 success:Bool msg_state_used:Bool account_activated:Bool + && t_Grams.validate_skip(cs) // gas_fees:Grams + && t_Ref_TrComputeInternal1.validate_skip(cs); // ^[ gas_used:(..) .. ] + } + return false; +} + +const TrComputePhase t_TrComputePhase; + +bool TrActionPhase::skip(vm::CellSlice& cs) const { + return cs.advance(3) // success:Bool valid:Bool no_funds:Bool + && t_AccStatusChange.skip(cs) // status_change:AccStatusChange + && t_Maybe_Grams.skip(cs) // total_fwd_fees:(Maybe Grams) + && t_Maybe_Grams.skip(cs) // total_action_fees:(Maybe Grams) + && cs.advance(32) // result_code:int32 + && Maybe{32}.skip(cs) // result_arg:(Maybe int32) + && cs.advance(16 * 4 + 256) // tot_actions:uint16 spec_actions:uint16 + // skipped_actions:uint16 msgs_created:uint16 + // action_list_hash:uint256 + && t_StorageUsedShort.skip(cs); // tot_msg_size:StorageUsedShort +} + +bool TrActionPhase::validate_skip(vm::CellSlice& cs) const { + return cs.advance(3) // success:Bool valid:Bool no_funds:Bool + && t_AccStatusChange.validate_skip(cs) // status_change:AccStatusChange + && t_Maybe_Grams.validate_skip(cs) // total_fwd_fees:(Maybe Grams) + && t_Maybe_Grams.validate_skip(cs) // total_action_fees:(Maybe Grams) + && cs.advance(32) // result_code:int32 + && Maybe{32}.validate_skip(cs) // result_arg:(Maybe int32) + && cs.advance(16 * 4 + 256) // tot_actions:uint16 spec_actions:uint16 + // skipped_actions:uint16 msgs_created:uint16 + // action_list_hash:uint256 + && t_StorageUsedShort.validate_skip(cs); // tot_msg_size:StorageUsed +} + +const TrActionPhase t_TrActionPhase; + +bool TrBouncePhase::skip(vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case tr_phase_bounce_negfunds: + return cs.advance(2); // tr_phase_bounce_negfunds$00 + case tr_phase_bounce_nofunds: + return cs.advance(2) // tr_phase_bounce_nofunds$01 + && t_StorageUsedShort.skip(cs) // msg_size:StorageUsedShort + && t_Grams.skip(cs); // req_fwd_fees:Grams + case tr_phase_bounce_ok: + return cs.advance(1) // tr_phase_bounce_ok$1 + && t_StorageUsedShort.skip(cs) // msg_size:StorageUsedShort + && t_Grams.skip(cs) // msg_fees:Grams + && t_Grams.skip(cs); // fwd_fees:Grams + } + return false; +} + +bool TrBouncePhase::validate_skip(vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case tr_phase_bounce_negfunds: + return cs.advance(2); // tr_phase_bounce_negfunds$00 + case tr_phase_bounce_nofunds: + return cs.advance(2) // tr_phase_bounce_nofunds$01 + && t_StorageUsedShort.validate_skip(cs) // msg_size:StorageUsedShort + && t_Grams.validate_skip(cs); // req_fwd_fees:Grams + case tr_phase_bounce_ok: + return cs.advance(1) // tr_phase_bounce_ok$1 + && t_StorageUsedShort.validate_skip(cs) // msg_size:StorageUsedShort + && t_Grams.validate_skip(cs) // msg_fees:Grams + && t_Grams.validate_skip(cs); // fwd_fees:Grams + } + return false; +} + +int TrBouncePhase::get_tag(const vm::CellSlice& cs) const { + if (cs.size() == 1) { + return (int)cs.prefetch_ulong(1) == 1 ? tr_phase_bounce_ok : -1; + } + int v = (int)cs.prefetch_ulong(2); + return v == 3 ? tr_phase_bounce_ok : v; +}; + +const TrBouncePhase t_TrBouncePhase; + +bool SplitMergeInfo::skip(vm::CellSlice& cs) const { + // cur_shard_pfx_len:(## 6) acc_split_depth:(##6) this_addr:uint256 sibling_addr:uint256 + return cs.advance(6 + 6 + 256 + 256); +} + +bool SplitMergeInfo::validate_skip(vm::CellSlice& cs) const { + if (!cs.have(6 + 6 + 256 + 256)) { + return false; + } + int cur_pfx_len = (int)cs.fetch_ulong(6); + int split_depth = (int)cs.fetch_ulong(6); + unsigned char this_addr[32], sibling_addr[32]; + if (!cs.fetch_bytes(this_addr, 32) || !cs.fetch_bytes(sibling_addr, 32)) { + return false; + } + // cur_pfx_len < split_depth, addresses match except in bit cur_pfx_len + if (cur_pfx_len >= split_depth) { + return false; + } + sibling_addr[cur_pfx_len >> 3] ^= (unsigned char)(0x80 >> (cur_pfx_len & 7)); + return !memcmp(this_addr, sibling_addr, 32); +} + +const SplitMergeInfo t_SplitMergeInfo; + +bool TransactionDescr::skip(vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case trans_ord: + return cs.advance(4 + 1) // trans_ord$0000 storage_first:Bool + && Maybe{}.skip(cs) // storage_ph:(Maybe TrStoragePhase) + && Maybe{}.skip(cs) // credit_ph:(Maybe TrCreditPhase) + && t_TrComputePhase.skip(cs) // compute_ph:TrComputePhase + && Maybe>{}.skip(cs) // action:(Maybe ^TrActionPhase) + && cs.advance(1) // aborted:Bool + && Maybe{}.skip(cs) // bounce:(Maybe TrBouncePhase) + && cs.advance(1); // destroyed:Bool + case trans_storage: + return cs.advance(4) // trans_storage$0001 + && t_TrStoragePhase.skip(cs); // storage_ph:TrStoragePhase + case trans_tick_tock: + return cs.advance(4) // trans_tick_tock$001 is_tock:Bool + && t_TrStoragePhase.skip(cs) // storage:TrStoragePhase + && t_TrComputePhase.skip(cs) // compute_ph:TrComputePhase + && Maybe>{}.skip(cs) // action:(Maybe ^TrActionPhase) + && cs.advance(2); // aborted:Bool destroyed:Bool + case trans_split_prepare: + return cs.advance(4) // trans_split_prepare$0100 + && t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo + && t_TrComputePhase.skip(cs) // compute_ph:TrComputePhase + && Maybe>{}.skip(cs) // action:(Maybe ^TrActionPhase) + && cs.advance(2); // aborted:Bool destroyed:Bool + case trans_split_install: + return cs.advance(4) // trans_split_install$0101 + && t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo + && t_Ref_Transaction.skip(cs) // prepare_transaction:^Transaction + && cs.advance(1); // installed:Bool + case trans_merge_prepare: + return cs.advance(4) // trans_merge_prepare$0110 + && t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo + && t_TrStoragePhase.skip(cs) // storage_ph:TrStoragePhase + && cs.advance(1); // aborted:Bool + case trans_merge_install: + return cs.advance(4) // trans_merge_install$0111 + && t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo + && t_Ref_Transaction.skip(cs) // prepare_transaction:^Transaction + && Maybe{}.skip(cs) // credit_ph:(Maybe TrCreditPhase) + && Maybe{}.skip(cs) // compute_ph:TrComputePhase + && Maybe>{}.skip(cs) // action:(Maybe ^TrActionPhase) + && cs.advance(2); // aborted:Bool destroyed:Bool + } + return false; +} + +bool TransactionDescr::validate_skip(vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case trans_ord: + return cs.advance(4 + 1) // trans_ord$0000 credit_first:Bool + && Maybe{}.validate_skip(cs) // storage_ph:(Maybe TrStoragePhase) + && Maybe{}.validate_skip(cs) // credit_ph:(Maybe TrCreditPhase) + && t_TrComputePhase.validate_skip(cs) // compute_ph:TrComputePhase + && Maybe>{}.validate_skip(cs) // action:(Maybe ^TrActionPhase) + && cs.advance(1) // aborted:Bool + && Maybe{}.validate_skip(cs) // bounce:(Maybe TrBouncePhase) + && cs.advance(1); // destroyed:Bool + case trans_storage: + return cs.advance(4) // trans_storage$0001 + && t_TrStoragePhase.validate_skip(cs); // storage_ph:TrStoragePhase + case trans_tick_tock: + return cs.advance(4) // trans_tick_tock$001 is_tock:Bool + && t_TrStoragePhase.validate_skip(cs) // storage:TrStoragePhase + && t_TrComputePhase.validate_skip(cs) // compute_ph:TrComputePhase + && Maybe>{}.validate_skip(cs) // action:(Maybe ^TrActionPhase) + && cs.advance(2); // aborted:Bool destroyed:Bool + case trans_split_prepare: + return cs.advance(4) // trans_split_prepare$0100 + && t_SplitMergeInfo.validate_skip(cs) // split_info:SplitMergeInfo + && t_TrComputePhase.validate_skip(cs) // compute_ph:TrComputePhase + && Maybe>{}.validate_skip(cs) // action:(Maybe ^TrActionPhase) + && cs.advance(2); // aborted:Bool destroyed:Bool + case trans_split_install: + return cs.advance(4) // trans_split_install$0101 + && t_SplitMergeInfo.validate_skip(cs) // split_info:SplitMergeInfo + && t_Ref_Transaction.validate_skip(cs) // prepare_transaction:^Transaction + && cs.advance(1); // installed:Bool + case trans_merge_prepare: + return cs.advance(4) // trans_merge_prepare$0110 + && t_SplitMergeInfo.validate_skip(cs) // split_info:SplitMergeInfo + && t_TrStoragePhase.validate_skip(cs) // storage_ph:TrStoragePhase + && cs.advance(1); // aborted:Bool + case trans_merge_install: + return cs.advance(4) // trans_merge_install$0111 + && t_SplitMergeInfo.validate_skip(cs) // split_info:SplitMergeInfo + && t_Ref_Transaction.validate_skip(cs) // prepare_transaction:^Transaction + && Maybe{}.validate_skip(cs) // credit_ph:(Maybe TrCreditPhase) + && Maybe{}.validate_skip(cs) // compute_ph:TrComputePhase + && Maybe>{}.validate_skip(cs) // action:(Maybe ^TrActionPhase) + && cs.advance(2); // aborted:Bool destroyed:Bool + } + return false; +} + +int TransactionDescr::get_tag(const vm::CellSlice& cs) const { + int t = (int)cs.prefetch_ulong(4); + return (t >= 0 && t <= 7) ? (t == 3 ? 2 : t) : -1; +} + +const TransactionDescr t_TransactionDescr; + +bool Transaction::skip(vm::CellSlice& cs) const { + return cs.advance( + 4 + 256 + 64 + 256 + 64 + 32 + + 15) // transaction$0110 account_addr:uint256 lt:uint64 prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32 outmsg_cnt:uint15 + && t_AccountStatus.skip(cs) // orig_status:AccountStatus + && t_AccountStatus.skip(cs) // end_status:AccountStatus + && Maybe>{}.skip(cs) // in_msg:(Maybe ^Message) + && HashmapE{15, t_Ref_Message}.skip(cs) // out_msgs:(HashmapE 15 ^Message) + && t_Grams.skip(cs) // total_fees:Grams + && t_RefCell.skip(cs) // state_update:^(MERKLE_UPDATE Account) + && RefTo{}.skip(cs); // description:^TransactionDescr +} + +bool Transaction::validate_skip(vm::CellSlice& cs) const { + return cs.fetch_ulong(4) == 6 // transaction$0110 + && + cs.advance( + 256 + 64 + 256 + 64 + 32 + + 15) // account_addr:uint256 lt:uint64 prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32 outmsg_cnt:uint15 + && t_AccountStatus.validate_skip(cs) // orig_status:AccountStatus + && t_AccountStatus.validate_skip(cs) // end_status:AccountStatus + && Maybe>{}.validate_skip(cs) // in_msg:(Maybe ^Message) + && HashmapE{15, t_Ref_Message}.validate_skip(cs) // out_msgs:(HashmapE 15 ^Message) + && t_Grams.validate_skip(cs) // total_fees:Grams + && t_RefCell.validate_skip(cs) // FIXME state_update:^(MERKLE_UPDATE Account) + && RefTo{}.validate_skip(cs); // description:^TransactionDescr +} + +bool Transaction::get_total_fees(vm::CellSlice&& cs, td::RefInt256& total_fees) const { + return cs.fetch_ulong(4) == 6 // transaction$0110 + && + cs.advance( + 256 + 64 + 256 + 64 + 32 + + 15) // account_addr:uint256 lt:uint64 prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32 outmsg_cnt:uint15 + && t_AccountStatus.skip(cs) // orig_status:AccountStatus + && t_AccountStatus.skip(cs) // end_status:AccountStatus + && Maybe>{}.skip(cs) // in_msg:(Maybe ^Message) + && HashmapE{15, t_Ref_Message}.skip(cs) // out_msgs:(HashmapE 15 ^Message) + && t_Grams.as_integer_skip_to(cs, total_fees); // total_fees:Grams +} + +const Transaction t_Transaction; +const RefTo t_Ref_Transaction; + +// leaf evaluation for (HashmapAug 64 ^Transaction Grams) +bool Aug_AccountTransactions::eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const { + auto cell_ref = cs.prefetch_ref(); + td::RefInt256 total_fees; + return cell_ref.not_null() && t_Transaction.get_total_fees(vm::load_cell_slice(std::move(cell_ref)), total_fees) && + t_Grams.store_integer_ref(cb, std::move(total_fees)); +} + +const Aug_AccountTransactions aug_AccountTransactions; +const HashmapAug t_AccountTransactions{64, aug_AccountTransactions}; + +const HashUpdate t_HashUpdate; +const RefTo t_Ref_HashUpdate; + +bool AccountBlock::skip(vm::CellSlice& cs) const { + return cs.advance(4 + 256) // acc_trans#4 account_addr:bits256 + && t_AccountTransactions.skip(cs) // transactions:(HashmapAug 64 ^Transaction Grams) + && cs.advance_refs(1); // state_update:^(HASH_UPDATE Account) +} + +bool AccountBlock::validate_skip(vm::CellSlice& cs) const { + return cs.fetch_ulong(4) == 4 // acc_trans#4 + && cs.advance(256) // account_addr:bits256 + && t_AccountTransactions.validate_skip(cs) // transactions:(HashmapAug 64 ^Transaction Grams) + && t_Ref_HashUpdate.validate_skip(cs); // state_update:^(HASH_UPDATE Account) +} + +bool AccountBlock::get_total_fees(vm::CellSlice&& cs, td::RefInt256& total_fees) const { + return cs.advance(4 + 256) // acc_trans#4 account_addr:bits256 + && t_AccountTransactions.extract_extra(cs) // transactions:(HashmapAug 64 ^Transaction Grams) + && t_Grams.as_integer_skip_to(cs, total_fees); +} + +const AccountBlock t_AccountBlock; + +bool Aug_ShardAccountBlocks::eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const { + td::RefInt256 total_fees; + return t_AccountBlock.get_total_fees(std::move(cs), total_fees) && + t_Grams.store_integer_ref(cb, std::move(total_fees)); +} + +const Aug_ShardAccountBlocks aug_ShardAccountBlocks; +const HashmapAugE t_ShardAccountBlocks{256, aug_ShardAccountBlocks}; // (HashmapAugE 256 AccountBlock Grams) + +bool ImportFees::validate_skip(vm::CellSlice& cs) const { + return t_Grams.validate_skip(cs) && t_CurrencyCollection.validate_skip(cs); +} + +bool ImportFees::skip(vm::CellSlice& cs) const { + return t_Grams.skip(cs) && t_CurrencyCollection.skip(cs); +} + +bool ImportFees::add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const { + return t_Grams.add_values(cb, cs1, cs2) && t_CurrencyCollection.add_values(cb, cs1, cs2); +} + +const ImportFees t_ImportFees; + +bool InMsg::skip(vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case msg_import_ext: + return cs.advance(3) // msg_import_ext$000 + && t_Ref_Message.skip(cs) // msg:^Message + && t_Ref_Transaction.skip(cs); // transaction:^Transaction + case msg_import_ihr: + return cs.advance(3) // msg_import_ihr$010 + && t_Ref_Message.skip(cs) // msg:^Message + && t_Ref_Transaction.skip(cs) // transaction:^Transaction + && t_Grams.skip(cs) // ihr_fee:Grams + && t_RefCell.skip(cs); // proof_created:^Cell + case msg_import_imm: + return cs.advance(3) // msg_import_imm$011 + && t_Ref_MsgEnvelope.skip(cs) // in_msg:^MsgEnvelope + && t_Ref_Transaction.skip(cs) // transaction:^Transaction + && t_Grams.skip(cs); // fwd_fee:Grams + case msg_import_fin: + return cs.advance(3) // msg_import_fin$100 + && t_Ref_MsgEnvelope.skip(cs) // in_msg:^MsgEnvelope + && t_Ref_Transaction.skip(cs) // transaction:^Transaction + && t_Grams.skip(cs); // fwd_fee:Grams + case msg_import_tr: + return cs.advance(3) // msg_import_tr$101 + && t_Ref_MsgEnvelope.skip(cs) // in_msg:^MsgEnvelope + && t_Ref_MsgEnvelope.skip(cs) // out_msg:^MsgEnvelope + && t_Grams.skip(cs); // transit_fee:Grams + case msg_discard_fin: + return cs.advance(3) // msg_discard_fin$110 + && t_Ref_MsgEnvelope.skip(cs) // in_msg:^MsgEnvelope + && cs.advance(64) // transaction_id:uint64 + && t_Grams.skip(cs); // fwd_fee:Grams + case msg_discard_tr: + return cs.advance(3) // msg_discard_tr$111 + && t_Ref_MsgEnvelope.skip(cs) // in_msg:^MsgEnvelope + && cs.advance(64) // transaction_id:uint64 + && t_Grams.skip(cs) // fwd_fee:Grams + && t_RefCell.skip(cs); // proof_delivered:^Cell + } + return false; +} + +bool InMsg::validate_skip(vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case msg_import_ext: + return cs.advance(3) // msg_import_ext$000 + && t_Ref_Message.validate_skip(cs) // msg:^Message + && t_Ref_Transaction.validate_skip(cs); // transaction:^Transaction + case msg_import_ihr: + return cs.advance(3) // msg_import_ihr$010 + && t_Ref_Message.validate_skip(cs) // msg:^Message + && t_Ref_Transaction.validate_skip(cs) // transaction:^Transaction + && t_Grams.validate_skip(cs) // ihr_fee:Grams + && t_RefCell.validate_skip(cs); // proof_created:^Cell + case msg_import_imm: + return cs.advance(3) // msg_import_imm$011 + && t_Ref_MsgEnvelope.validate_skip(cs) // in_msg:^MsgEnvelope + && t_Ref_Transaction.validate_skip(cs) // transaction:^Transaction + && t_Grams.validate_skip(cs); // fwd_fee:Grams + case msg_import_fin: + return cs.advance(3) // msg_import_fin$100 + && t_Ref_MsgEnvelope.validate_skip(cs) // in_msg:^MsgEnvelope + && t_Ref_Transaction.validate_skip(cs) // transaction:^Transaction + && t_Grams.validate_skip(cs); // fwd_fee:Grams + case msg_import_tr: + return cs.advance(3) // msg_import_tr$101 + && t_Ref_MsgEnvelope.validate_skip(cs) // in_msg:^MsgEnvelope + && t_Ref_MsgEnvelope.validate_skip(cs) // out_msg:^MsgEnvelope + && t_Grams.validate_skip(cs); // transit_fee:Grams + case msg_discard_fin: + return cs.advance(3) // msg_discard_fin$110 + && t_Ref_MsgEnvelope.validate_skip(cs) // in_msg:^MsgEnvelope + && cs.advance(64) // transaction_id:uint64 + && t_Grams.validate_skip(cs); // fwd_fee:Grams + case msg_discard_tr: + return cs.advance(3) // msg_discard_tr$111 + && t_Ref_MsgEnvelope.validate_skip(cs) // in_msg:^MsgEnvelope + && cs.advance(64) // transaction_id:uint64 + && t_Grams.validate_skip(cs) // fwd_fee:Grams + && t_RefCell.validate_skip(cs); // proof_delivered:^Cell + } + return false; +} + +bool InMsg::get_import_fees(vm::CellBuilder& cb, vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case msg_import_ext: // inbound external message + return t_ImportFees.null_value(cb); // external messages have no value and no import fees + case msg_import_ihr: // IHR-forwarded internal message to its final destination + if (cs.advance(3) && cs.size_refs() >= 3) { + auto msg_cs = load_cell_slice(cs.fetch_ref()); + CommonMsgInfo::Record_int_msg_info msg_info; + td::RefInt256 ihr_fee; + vm::CellBuilder aux; + // sort of Prolog-style in C++ + return t_Message.extract_info(msg_cs) && t_CommonMsgInfo.unpack(msg_cs, msg_info) && + cs.fetch_ref().not_null() && (ihr_fee = t_Grams.as_integer_skip(cs)).not_null() && + cs.fetch_ref().not_null() && !cmp(ihr_fee, t_Grams.as_integer(*msg_info.ihr_fee)) && + cb.append_cellslice_bool(msg_info.ihr_fee) // fees_collected := ihr_fee + && aux.append_cellslice_bool(msg_info.ihr_fee) && t_ExtraCurrencyCollection.null_value(aux) && + t_CurrencyCollection.add_values(cb, aux.as_cellslice_ref().write(), + msg_info.value.write()); // value_imported := ihr_fee + value + } + return false; + case msg_import_imm: // internal message re-imported from this very block + if (cs.advance(3) && cs.size_refs() >= 2) { + return cs.fetch_ref().not_null() && cs.fetch_ref().not_null() && + cb.append_cellslice_bool(t_Grams.fetch(cs)) // fees_collected := fwd_fees + && t_CurrencyCollection.null_value(cb); // value_imported := 0 + } + return false; + case msg_import_fin: // internal message delivered to its final destination in this block + if (cs.advance(3) && cs.size_refs() >= 2) { + auto msg_env_cs = load_cell_slice(cs.fetch_ref()); + MsgEnvelope::Record in_msg; + td::RefInt256 fwd_fee, fwd_fee_remaining, value_grams, ihr_fee; + if (!(t_MsgEnvelope.unpack(msg_env_cs, in_msg) && cs.fetch_ref().not_null() && + t_Grams.as_integer_skip_to(cs, fwd_fee) && + (fwd_fee_remaining = t_Grams.as_integer(in_msg.fwd_fee_remaining)).not_null() && + !(cmp(fwd_fee, fwd_fee_remaining)))) { + return false; + } + auto msg_cs = load_cell_slice(std::move(in_msg.msg)); + CommonMsgInfo::Record_int_msg_info msg_info; + return t_Message.extract_info(msg_cs) && t_CommonMsgInfo.unpack(msg_cs, msg_info) && + cb.append_cellslice_bool(in_msg.fwd_fee_remaining) // fees_collected := fwd_fee_remaining + && t_Grams.as_integer_skip_to(msg_info.value.write(), value_grams) && + (ihr_fee = t_Grams.as_integer(std::move(msg_info.ihr_fee))).not_null() && + t_Grams.store_integer_ref(cb, value_grams + ihr_fee + fwd_fee_remaining) && + cb.append_cellslice_bool( + msg_info.value.write()); // value_imported = msg.value + msg.ihr_fee + fwd_fee_remaining + } + return false; + case msg_import_tr: // transit internal message + if (cs.advance(3) && cs.size_refs() >= 2) { + auto msg_env_cs = load_cell_slice(cs.fetch_ref()); + MsgEnvelope::Record in_msg; + td::RefInt256 transit_fee, fwd_fee_remaining, value_grams, ihr_fee; + if (!(t_MsgEnvelope.unpack(msg_env_cs, in_msg) && cs.fetch_ref().not_null() && + t_Grams.as_integer_skip_to(cs, transit_fee) && + (fwd_fee_remaining = t_Grams.as_integer(in_msg.fwd_fee_remaining)).not_null() && + cmp(transit_fee, fwd_fee_remaining) <= 0)) { + return false; + } + auto msg_cs = load_cell_slice(in_msg.msg); + CommonMsgInfo::Record_int_msg_info msg_info; + return t_Message.extract_info(msg_cs) && t_CommonMsgInfo.unpack(msg_cs, msg_info) && + t_Grams.store_integer_ref(cb, std::move(transit_fee)) // fees_collected := transit_fees + && t_Grams.as_integer_skip_to(msg_info.value.write(), value_grams) && + (ihr_fee = t_Grams.as_integer(std::move(msg_info.ihr_fee))).not_null() && + t_Grams.store_integer_ref(cb, value_grams + ihr_fee + fwd_fee_remaining) && + cb.append_cellslice_bool( + msg_info.value.write()); // value_imported = msg.value + msg.ihr_fee + fwd_fee_remaining + } + return false; + case msg_discard_fin: // internal message discarded at its final destination because of previous IHR delivery + if (cs.advance(3) && cs.size_refs() >= 1) { + Ref fwd_fee; + return cs.fetch_ref().not_null() && cs.advance(64) && (fwd_fee = t_Grams.fetch(cs)).not_null() && + cb.append_cellslice_bool(fwd_fee) // fees_collected := fwd_fee + && cb.append_cellslice_bool(std::move(fwd_fee)) && + t_ExtraCurrencyCollection.null_value(cb); // value_imported := fwd_fee + } + return false; + case msg_discard_tr: // internal message discarded at an intermediate destination + if (cs.advance(3) && cs.size_refs() >= 2) { + Ref fwd_fee; + return cs.fetch_ref().not_null() && cs.advance(64) && (fwd_fee = t_Grams.fetch(cs)).not_null() && + cs.fetch_ref().not_null() && cb.append_cellslice_bool(fwd_fee) // fees_collected := fwd_fee + && cb.append_cellslice_bool(std::move(fwd_fee)) && + t_ExtraCurrencyCollection.null_value(cb); // value_imported := fwd_fee + } + return false; + } + return false; +} + +const InMsg t_InMsg; + +const Aug_InMsgDescr aug_InMsgDescr; +const InMsgDescr t_InMsgDescr; + +bool OutMsg::skip(vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case msg_export_ext: + return cs.advance(3) // msg_export_ext$000 + && t_Ref_Message.skip(cs) // msg:^Message + && t_Ref_Transaction.skip(cs); // transaction:^Transaction + case msg_export_imm: + return cs.advance(3) // msg_export_imm$010 + && t_Ref_MsgEnvelope.skip(cs) // out_msg:^MsgEnvelope + && t_Ref_Transaction.skip(cs) // transaction:^Transaction + && RefTo{}.skip(cs); // reimport:^InMsg + case msg_export_new: + return cs.advance(3) // msg_export_new$001 + && t_Ref_MsgEnvelope.skip(cs) // out_msg:^MsgEnvelope + && t_Ref_Transaction.skip(cs); // transaction:^Transaction + case msg_export_tr: + return cs.advance(3) // msg_export_tr$011 + && t_Ref_MsgEnvelope.skip(cs) // out_msg:^MsgEnvelope + && RefTo{}.skip(cs); // imported:^InMsg + case msg_export_deq_imm: + return cs.advance(3) // msg_export_deq_imm$100 + && t_Ref_MsgEnvelope.skip(cs) // out_msg:^MsgEnvelope + && RefTo{}.skip(cs); // reimport:^InMsg + case msg_export_deq: + return cs.advance(3) // msg_export_deq$110 + && t_Ref_MsgEnvelope.skip(cs) // out_msg:^MsgEnvelope + && cs.advance(64); // import_block_lt:uint64 + case msg_export_tr_req: + return cs.advance(3) // msg_export_tr_req$111 + && t_Ref_MsgEnvelope.skip(cs) // out_msg:^MsgEnvelope + && RefTo{}.skip(cs); // imported:^InMsg + } + return false; +} + +bool OutMsg::validate_skip(vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case msg_export_ext: + return cs.advance(3) // msg_export_ext$000 + && t_Ref_Message.validate_skip(cs) // msg:^Message + && t_Ref_Transaction.validate_skip(cs); // transaction:^Transaction + case msg_export_imm: + return cs.advance(3) // msg_export_imm$010 + && t_Ref_MsgEnvelope.validate_skip(cs) // out_msg:^MsgEnvelope + && t_Ref_Transaction.validate_skip(cs) // transaction:^Transaction + && RefTo{}.validate_skip(cs); // reimport:^InMsg + case msg_export_new: + return cs.advance(3) // msg_export_new$001 + && t_Ref_MsgEnvelope.validate_skip(cs) // out_msg:^MsgEnvelope + && t_Ref_Transaction.validate_skip(cs); // transaction:^Transaction + case msg_export_tr: + return cs.advance(3) // msg_export_tr$011 + && t_Ref_MsgEnvelope.validate_skip(cs) // out_msg:^MsgEnvelope + && RefTo{}.validate_skip(cs); // imported:^InMsg + case msg_export_deq_imm: + return cs.advance(3) // msg_export_deq_imm$100 + && t_Ref_MsgEnvelope.validate_skip(cs) // out_msg:^MsgEnvelope + && RefTo{}.validate_skip(cs); // reimport:^InMsg + case msg_export_deq: + return cs.advance(3) // msg_export_deq$110 + && t_Ref_MsgEnvelope.validate_skip(cs) // out_msg:^MsgEnvelope + && cs.advance(64); // import_block_lt:uint64 + case msg_export_tr_req: + return cs.advance(3) // msg_export_tr_req$111 + && t_Ref_MsgEnvelope.validate_skip(cs) // out_msg:^MsgEnvelope + && RefTo{}.validate_skip(cs); // imported:^InMsg + } + return false; +} + +bool OutMsg::get_export_value(vm::CellBuilder& cb, vm::CellSlice& cs) const { + switch (get_tag(cs)) { + case msg_export_ext: // external outbound message carries no value + if (cs.have(3, 2)) { + return t_CurrencyCollection.null_value(cb); + } + return false; + case msg_export_imm: // outbound internal message delivered in this very block, no value exported + return cs.have(3, 3) && t_CurrencyCollection.null_value(cb); + case msg_export_deq_imm: // dequeuing record for outbound message delivered in this very block, no value exported + return cs.have(3, 2) && t_CurrencyCollection.null_value(cb); + case msg_export_deq: // dequeueing record for outbound message, no exported value + return cs.have(3, 1) && t_CurrencyCollection.null_value(cb); + case msg_export_new: // newly-generated outbound internal message, queued + case msg_export_tr: // transit internal message, queued + case msg_export_tr_req: // transit internal message, re-queued from this shardchain + if (cs.advance(3) && cs.size_refs() >= 2) { + auto msg_env_cs = load_cell_slice(cs.fetch_ref()); + MsgEnvelope::Record out_msg; + if (!(cs.fetch_ref().not_null() && t_MsgEnvelope.unpack(msg_env_cs, out_msg))) { + return false; + } + auto msg_cs = load_cell_slice(std::move(out_msg.msg)); + CommonMsgInfo::Record_int_msg_info msg_info; + td::RefInt256 value_grams, ihr_fee, fwd_fee_remaining; + return t_Message.extract_info(msg_cs) && t_CommonMsgInfo.unpack(msg_cs, msg_info) && + (value_grams = t_Grams.as_integer_skip(msg_info.value.write())).not_null() && + (ihr_fee = t_Grams.as_integer(std::move(msg_info.ihr_fee))).not_null() && + (fwd_fee_remaining = t_Grams.as_integer(out_msg.fwd_fee_remaining)).not_null() && + t_Grams.store_integer_ref(cb, value_grams + ihr_fee + fwd_fee_remaining) && + cb.append_cellslice_bool(std::move(msg_info.value)); + // exported value = msg.value + msg.ihr_fee + fwd_fee_remaining + } + return false; + } + return false; +} + +bool OutMsg::get_created_lt(vm::CellSlice& cs, unsigned long long& created_lt) const { + switch (get_tag(cs)) { + case msg_export_ext: + if (cs.have(3, 1)) { + auto msg_cs = load_cell_slice(cs.prefetch_ref()); + return t_Message.get_created_lt(msg_cs, created_lt); + } else { + return false; + } + case msg_export_imm: + case msg_export_new: + case msg_export_tr: + case msg_export_deq: + case msg_export_deq_imm: + case msg_export_tr_req: + if (cs.have(3, 1)) { + auto out_msg_cs = load_cell_slice(cs.prefetch_ref()); + return t_MsgEnvelope.get_created_lt(out_msg_cs, created_lt); + } else { + return false; + } + } + return false; +} + +const OutMsg t_OutMsg; + +const Aug_OutMsgDescr aug_OutMsgDescr; +const OutMsgDescr t_OutMsgDescr; + +bool EnqueuedMsg::validate_skip(vm::CellSlice& cs) const { + return cs.advance(64) && t_Ref_MsgEnvelope.validate_skip(cs); +} + +const EnqueuedMsg t_EnqueuedMsg; + +bool Aug_OutMsgQueue::eval_fork(vm::CellBuilder& cb, vm::CellSlice& left_cs, vm::CellSlice& right_cs) const { + unsigned long long x, y; + return left_cs.fetch_ulong_bool(64, x) && right_cs.fetch_ulong_bool(64, y) && + cb.store_ulong_rchk_bool(std::min(x, y), 64); +} + +bool Aug_OutMsgQueue::eval_empty(vm::CellBuilder& cb) const { + return cb.store_long_bool(0, 64); +} + +bool Aug_OutMsgQueue::eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const { + Ref msg_env; + unsigned long long created_lt; + return cs.fetch_ref_to(msg_env) && t_MsgEnvelope.get_created_lt(load_cell_slice(std::move(msg_env)), created_lt) && + cb.store_ulong_rchk_bool(created_lt, 64); +} + +const Aug_OutMsgQueue aug_OutMsgQueue; +const OutMsgQueue t_OutMsgQueue; + +const ProcessedUpto t_ProcessedUpto; +const HashmapE t_ProcessedInfo{96, t_ProcessedUpto}; +const HashmapE t_IhrPendingInfo{256, t_uint128}; + +// _ out_queue:OutMsgQueue proc_info:ProcessedInfo = OutMsgQueueInfo; +bool OutMsgQueueInfo::skip(vm::CellSlice& cs) const { + return t_OutMsgQueue.skip(cs) && t_ProcessedInfo.skip(cs) && t_IhrPendingInfo.skip(cs); +} + +bool OutMsgQueueInfo::validate_skip(vm::CellSlice& cs) const { + return t_OutMsgQueue.validate_skip(cs) && t_ProcessedInfo.validate_skip(cs) && t_IhrPendingInfo.validate_skip(cs); +} + +const OutMsgQueueInfo t_OutMsgQueueInfo; +const RefTo t_Ref_OutMsgQueueInfo; + +bool ExtBlkRef::unpack(vm::CellSlice& cs, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt) const { + block::gen::ExtBlkRef::Record data; + if (!tlb::unpack(cs, data)) { + blkid.invalidate(); + return false; + } + blkid.id = ton::BlockId{ton::masterchainId, ton::shardIdAll, data.seq_no}; + blkid.root_hash = data.root_hash; + blkid.file_hash = data.file_hash; + if (end_lt) { + *end_lt = data.end_lt; + } + return true; +} + +bool ExtBlkRef::unpack(Ref cs_ref, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt) const { + block::gen::ExtBlkRef::Record data; + if (!tlb::csr_unpack_safe(std::move(cs_ref), data)) { + blkid.invalidate(); + return false; + } + blkid.id = ton::BlockId{ton::masterchainId, ton::shardIdAll, data.seq_no}; + blkid.root_hash = data.root_hash; + blkid.file_hash = data.file_hash; + if (end_lt) { + *end_lt = data.end_lt; + } + return true; +} + +const ExtBlkRef t_ExtBlkRef; +const BlkMasterInfo t_BlkMasterInfo; + +bool ShardIdent::validate_skip(vm::CellSlice& cs) const { + int shard_pfx_len, workchain_id; + unsigned long long shard_pfx; + if (cs.fetch_ulong(2) == 0 && cs.fetch_uint_to(6, shard_pfx_len) && cs.fetch_int_to(32, workchain_id) && + workchain_id != ton::workchainInvalid && cs.fetch_uint_to(64, shard_pfx)) { + auto pow2 = (1ULL << (63 - shard_pfx_len)); + if (!(shard_pfx & (pow2 - 1))) { + return true; + } + } + return false; +} + +bool ShardIdent::Record::check() const { + return workchain_id != ton::workchainInvalid && !(shard_prefix & ((1ULL << (63 - shard_pfx_bits)) - 1)); +} + +bool ShardIdent::unpack(vm::CellSlice& cs, ShardIdent::Record& data) const { + if (cs.fetch_ulong(2) == 0 && cs.fetch_uint_to(6, data.shard_pfx_bits) && cs.fetch_int_to(32, data.workchain_id) && + cs.fetch_uint_to(64, data.shard_prefix) && data.check()) { + return true; + } else { + data.invalidate(); + return false; + } +} + +bool ShardIdent::pack(vm::CellBuilder& cb, const Record& data) const { + return data.check() && cb.store_ulong_rchk_bool(0, 2) && cb.store_ulong_rchk_bool(data.shard_pfx_bits, 6) && + cb.store_long_rchk_bool(data.workchain_id, 32) && cb.store_ulong_rchk_bool(data.shard_prefix, 64); +} + +bool ShardIdent::unpack(vm::CellSlice& cs, ton::WorkchainId& workchain, ton::ShardId& shard) const { + int bits; + unsigned long long pow2; + auto assign = [](auto& a, auto b) { return a = b; }; + auto assign_or = [](auto& a, auto b) { return a |= b; }; + return cs.fetch_ulong(2) == 0 // shard_ident$00 + && cs.fetch_uint_leq(60, bits) // shard_pfx_bits:(#<= 60) + && assign(pow2, (1ULL << (63 - bits))) // (power) + && cs.fetch_int_to(32, workchain) // workchain_id:int32 + && cs.fetch_uint_to(64, shard) // shard_prefix:uint64 + && workchain != ton::workchainInvalid && !(shard & (2 * pow2 - 1)) && assign_or(shard, pow2); +} + +bool ShardIdent::unpack(vm::CellSlice& cs, ton::ShardIdFull& data) const { + return unpack(cs, data.workchain, data.shard); +} + +bool ShardIdent::pack(vm::CellBuilder& cb, ton::WorkchainId workchain, ton::ShardId shard) const { + int bits = ton::shard_prefix_length(shard); + return workchain != ton::workchainInvalid // check workchain + && shard // check shard + && cb.store_long_bool(0, 2) // shard_ident$00 + && cb.store_uint_leq(60, bits) // shard_pfx_bits:(#<= 60) + && cb.store_long_bool(workchain, 32) // workchain_id:int32 + && cb.store_long_bool(shard & (shard - 1), 64); // shard_prefix:uint64 +} + +bool ShardIdent::pack(vm::CellBuilder& cb, ton::ShardIdFull data) const { + return pack(cb, data.workchain, data.shard); +} + +const ShardIdent t_ShardIdent; + +bool BlockIdExt::validate_skip(vm::CellSlice& cs) const { + return t_ShardIdent.validate_skip(cs) && cs.advance(32 + 256 * 2); +} + +bool BlockIdExt::unpack(vm::CellSlice& cs, ton::BlockIdExt& data) const { + return t_ShardIdent.unpack(cs, data.id.workchain, data.id.shard) // block_id_ext$_ shard_id:ShardIdent + && cs.fetch_uint_to(32, data.id.seqno) // seq_no:uint32 + && cs.fetch_bits_to(data.root_hash) // root_hash:bits256 + && cs.fetch_bits_to(data.file_hash); // file_hash:bits256 +} + +bool BlockIdExt::pack(vm::CellBuilder& cb, const ton::BlockIdExt& data) const { + return t_ShardIdent.pack(cb, data.id.workchain, data.id.shard) // block_id_ext$_ shard_id:ShardIdent + && cb.store_long_bool(data.id.seqno, 32) // seq_no:uint32 + && cb.store_bits_bool(data.root_hash) // root_hash:bits256 + && cb.store_bits_bool(data.file_hash); // file_hash:bits256 +} + +const BlockIdExt t_BlockIdExt; + +bool ShardState::skip(vm::CellSlice& cs) const { + return get_tag(cs) == shard_state && cs.advance(64) // shard_state#9023afe1 blockchain_id:int32 + && t_ShardIdent.skip(cs) // shard_id:ShardIdent + && cs.advance(32 + 32 + 32 + 64 + + 32) // seq_no:int32 vert_seq_no:# gen_utime:uint32 gen_lt:uint64 min_ref_mc_seqno:uint32 + && t_Ref_OutMsgQueueInfo.skip(cs) // out_msg_queue_info:^OutMsgQueueInfo + && cs.advance(1) // before_split:Bool + && t_ShardAccounts.skip(cs) // accounts:ShardAccounts + && + cs.advance_refs( + 1) // ^[ total_balance:CurrencyCollection total_validator_fees:CurrencyCollection libraries:(HashmapE 256 LibDescr) master_ref:(Maybe BlkMasterInfo) ] + && Maybe>{}.skip(cs); // custom:(Maybe ^McStateExtra) +} + +bool ShardState::validate_skip(vm::CellSlice& cs) const { + int seq_no; + return get_tag(cs) == shard_state && cs.advance(64) // shard_state#9023afde blockchain_id:int32 + && t_ShardIdent.validate_skip(cs) // shard_id:ShardIdent + && cs.fetch_int_to(32, seq_no) // seq_no:int32 + && seq_no >= -1 // { seq_no >= -1 } + && cs.advance(32 + 32 + 64 + 32) // vert_seq_no:# gen_utime:uint32 gen_lt:uint64 min_ref_mc_seqno:uint32 + && t_Ref_OutMsgQueueInfo.validate_skip(cs) // out_msg_queue_info:^OutMsgQueueInfo + && cs.advance(1) // before_split:Bool + && t_ShardAccounts.validate_skip(cs) // accounts:ShardAccounts + && + t_ShardState_aux.validate_skip_ref( + cs) // ^[ total_balance:CurrencyCollection total_validator_fees:CurrencyCollection libraries:(HashmapE 256 LibDescr) master_ref:(Maybe BlkMasterInfo) ] + && Maybe>{}.validate_skip(cs); // custom:(Maybe ^McStateExtra) +} + +const ShardState t_ShardState; + +bool ShardState_aux::skip(vm::CellSlice& cs) const { + return cs.advance(128) // overload_history:uint64 underload_history:uint64 + && t_CurrencyCollection.skip(cs) // total_balance:CurrencyCollection + && t_CurrencyCollection.skip(cs) // total_validator_fees:CurrencyCollection + && HashmapE{256, t_LibDescr}.skip(cs) // libraries:(HashmapE 256 LibDescr) + && Maybe{}.skip(cs); // master_ref:(Maybe BlkMasterInfo) +} + +bool ShardState_aux::validate_skip(vm::CellSlice& cs) const { + return cs.advance(128) // overload_history:uint64 underload_history:uint64 + && t_CurrencyCollection.validate_skip(cs) // total_balance:CurrencyCollection + && t_CurrencyCollection.validate_skip(cs) // total_validator_fees:CurrencyCollection + && HashmapE{256, t_LibDescr}.validate_skip(cs) // libraries:(HashmapE 256 LibDescr) + && Maybe{}.validate_skip(cs); // master_ref:(Maybe BlkMasterInfo) +} + +const ShardState_aux t_ShardState_aux; + +bool LibDescr::skip(vm::CellSlice& cs) const { + return cs.advance(2) // shared_lib_descr$00 + && cs.fetch_ref().not_null() // lib:^Cell + && Hashmap{256, t_True}.skip(cs); // publishers:(Hashmap 256 False) +} + +bool LibDescr::validate_skip(vm::CellSlice& cs) const { + return get_tag(cs) == shared_lib_descr && cs.advance(2) // shared_lib_descr$00 + && cs.fetch_ref().not_null() // lib:^Cell + && Hashmap{256, t_True}.validate_skip(cs); // publishers:(Hashmap 256 False) +} + +const LibDescr t_LibDescr; + +bool BlkPrevInfo::skip(vm::CellSlice& cs) const { + return t_ExtBlkRef.skip(cs) // prev_blk_info$_ {merged:#} prev:ExtBlkRef + && (!merged || t_ExtBlkRef.skip(cs)); // prev_alt:merged?ExtBlkRef +} + +bool BlkPrevInfo::validate_skip(vm::CellSlice& cs) const { + return t_ExtBlkRef.validate_skip(cs) // prev_blk_info$_ {merged:#} prev:ExtBlkRef + && (!merged || t_ExtBlkRef.validate_skip(cs)); // prev_alt:merged?ExtBlkRef +} + +const BlkPrevInfo t_BlkPrevInfo_0{0}; + +bool McStateExtra::skip(vm::CellSlice& cs) const { + return block::gen::t_McStateExtra.skip(cs); +} + +bool McStateExtra::validate_skip(vm::CellSlice& cs) const { + return block::gen::t_McStateExtra.validate_skip(cs); // FIXME +} + +const McStateExtra t_McStateExtra; + +const KeyExtBlkRef t_KeyExtBlkRef; + +bool KeyMaxLt::add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const { + bool key1, key2; + unsigned long long lt1, lt2; + return cs1.fetch_bool_to(key1) && cs1.fetch_ulong_bool(64, lt1) // cs1 => _ key:Bool max_end_lt:uint64 = KeyMaxLt; + && cs2.fetch_bool_to(key2) && cs2.fetch_ulong_bool(64, lt2) // cs2 => _ key:Bool max_end_lt:uint64 = KeyMaxLt; + && cb.store_bool_bool(key1 | key2) && cb.store_long_bool(std::max(lt1, lt2), 64); +} + +const KeyMaxLt t_KeyMaxLt; + +bool Aug_OldMcBlocksInfo::eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const { + return cs.have(65) && cb.append_bitslice(cs.prefetch_bits(65)); // copy first 1+64 bits +}; + +const Aug_OldMcBlocksInfo aug_OldMcBlocksInfo; + +} // namespace tlb +} // namespace block diff --git a/ton-test-liteclient-full/lite-client/crypto/block/block-parse.h b/ton-test-liteclient-full/lite-client/crypto/block/block-parse.h new file mode 100644 index 0000000..8c07b39 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/block/block-parse.h @@ -0,0 +1,1036 @@ +#pragma once +#include "common/refcnt.hpp" +#include "vm/cells.h" +#include "vm/cellslice.h" +#include "vm/dict.h" +#include "vm/boc.h" +#include "block/block.h" +#include +#include "tl/tlblib.hpp" +#include "td/utils/bits.h" +#include "td/utils/StringBuilder.h" +#include "ton/ton-types.h" + +namespace block { + +using td::Ref; + +namespace tlb { +using namespace ::tlb; + +struct Anycast final : TLB { + int get_size(const vm::CellSlice& cs) const override { + return cs.have(5) ? 5 + (int)cs.prefetch_ulong(5) : -1; + } + bool skip_get_depth(vm::CellSlice& cs, int& depth) const { + return cs.fetch_uint_leq(30, depth) && cs.advance(depth); + } +}; + +extern const Anycast t_Anycast; + +struct Maybe_Anycast final : public Maybe { + bool skip_get_depth(vm::CellSlice& cs, int& depth) const; +}; + +extern const Maybe_Anycast t_Maybe_Anycast; + +struct VarUInteger final : TLB_Complex { + int n, ln; + VarUInteger(int _n) : n(_n) { + ln = 32 - td::count_leading_zeroes32(n - 1); + } + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; + unsigned long long as_uint(const vm::CellSlice& cs) const override; + bool null_value(vm::CellBuilder& cb) const override { + return cb.store_zeroes_bool(ln); + } + bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override; + unsigned precompute_integer_size(const td::BigInt256& value) const; + unsigned precompute_integer_size(td::RefInt256 value) const; +}; + +extern const VarUInteger t_VarUInteger_3, t_VarUInteger_7, t_VarUInteger_16, t_VarUInteger_32; + +struct VarUIntegerPos final : TLB_Complex { + int n, ln; + VarUIntegerPos(int _n) : n(_n) { + ln = 32 - td::count_leading_zeroes32(n - 1); + } + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; + unsigned long long as_uint(const vm::CellSlice& cs) const override; + bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override; +}; + +extern const VarUIntegerPos t_VarUIntegerPos_16, t_VarUIntegerPos_32; + +struct VarInteger final : TLB_Complex { + int n, ln; + VarInteger(int _n) : n(_n) { + ln = 32 - td::count_leading_zeroes32(n - 1); + } + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; + long long as_int(const vm::CellSlice& cs) const override; + bool null_value(vm::CellBuilder& cb) const override { + return cb.store_zeroes_bool(ln); + } + bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override; +}; + +struct VarIntegerNz final : TLB_Complex { + int n, ln; + VarIntegerNz(int _n) : n(_n) { + ln = 32 - td::count_leading_zeroes32(n - 1); + } + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; + long long as_int(const vm::CellSlice& cs) const override; + bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override; +}; + +struct Unary final : TLB { + int get_size(const vm::CellSlice& cs) const override { + return cs.count_leading(1) + 1; + } + bool validate_skip(vm::CellSlice& cs, int& n) const { + return cs.advance((n = cs.count_leading(1)) + 1); + } + bool skip(vm::CellSlice& cs, int& n) const { + return validate_skip(cs, n); + } + bool validate_skip(vm::CellSlice& cs) const override { + return cs.advance(get_size(cs)); + } + bool skip(vm::CellSlice& cs) const override { + return validate_skip(cs); + } + bool validate(const vm::CellSlice& cs) const override { + return cs.have(get_size(cs)); + } +}; + +extern const Unary t_Unary; + +struct HmLabel final : TLB_Complex { + enum { hml_short = 0, hml_long = 2, hml_same = 3 }; + int m; // max size + HmLabel(int _m) : m(_m) { + } + bool validate_skip(vm::CellSlice& cs, int& n) const; + bool skip(vm::CellSlice& cs, int& n) const { + return validate_skip(cs, n); + } + bool skip(vm::CellSlice& cs) const override { + int n; + return skip(cs, n); + } + bool validate_skip(vm::CellSlice& cs) const override { + int n; + return validate_skip(cs, n); + } + int get_tag(const vm::CellSlice& cs) const override; +}; + +struct Hashmap final : TLB_Complex { + const TLB& value_type; + int n; + Hashmap(int _n, const TLB& _val_type) : value_type(_val_type), n(_n) { + } + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; +}; + +struct HashmapNode final : TLB_Complex { + enum { hmn_leaf = 0, hmn_fork = 1 }; + const TLB& value_type; + int n; + HashmapNode(int _n, const TLB& _val_type) : value_type(_val_type), n(_n) { + } + int get_size(const vm::CellSlice& cs) const override; + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override { + return n > 0 ? hmn_fork : n; + } +}; + +struct HashmapE final : TLB { + enum { hme_empty = 0, hme_root = 1 }; + Hashmap root_type; + HashmapE(int _n, const TLB& _val_type) : root_type(_n, _val_type) { + } + int get_size(const vm::CellSlice& cs) const override; + bool validate(const vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override { + return (int)cs.prefetch_ulong(1); + } + bool null_value(vm::CellBuilder& cb) const override { + return cb.store_zeroes_bool(1); + } + bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override; + int sub_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override; + bool add_values_ref(Ref& res, Ref arg1, Ref arg2) const; + int sub_values_ref(Ref& res, Ref arg1, Ref arg2) const; + bool store_ref(vm::CellBuilder& cb, Ref arg) const; +}; + +struct AugmentationCheckData : vm::dict::AugmentationData { + const TLB& value_type; + const TLB& extra_type; + AugmentationCheckData(const TLB& val_type, const TLB& ex_type) : value_type(val_type), extra_type(ex_type) { + } + bool skip_extra(vm::CellSlice& cs) const override { + return extra_type.skip(cs); + } + bool eval_fork(vm::CellBuilder& cb, vm::CellSlice& left_cs, vm::CellSlice& right_cs) const override { + return extra_type.add_values(cb, left_cs, right_cs); + } + bool eval_empty(vm::CellBuilder& cb) const override { + return extra_type.null_value(cb); + } +}; + +struct HashmapAug final : TLB_Complex { + const AugmentationCheckData& aug; + int n; + HashmapAug(int _n, const AugmentationCheckData& _aug) : aug(_aug), n(_n) { + } + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + bool extract_extra(vm::CellSlice& cs) const; +}; + +struct HashmapAugNode final : TLB_Complex { + enum { ahmn_leaf = 0, ahmn_fork = 1 }; + const AugmentationCheckData& aug; + int n; + HashmapAugNode(int _n, const AugmentationCheckData& _aug) : aug(_aug), n(_n) { + } + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override { + return n > 0 ? ahmn_fork : n; + } +}; + +struct HashmapAugE final : TLB_Complex { + enum { ahme_empty = 0, ahme_root = 1 }; + HashmapAug root_type; + HashmapAugE(int _n, const AugmentationCheckData& _aug) : root_type(_n, std::move(_aug)) { + } + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + bool extract_extra(vm::CellSlice& cs) const; + int get_tag(const vm::CellSlice& cs) const override { + return (int)cs.prefetch_ulong(1); + } +}; + +struct Grams final : TLB_Complex { + bool validate_skip(vm::CellSlice& cs) const override; + td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; + bool null_value(vm::CellBuilder& cb) const override; + bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override; + unsigned precompute_size(const td::BigInt256& value) const; + unsigned precompute_size(td::RefInt256 value) const; +}; + +extern const Grams t_Grams; + +struct MsgAddressInt final : TLB_Complex { + enum { addr_std = 2, addr_var = 3 }; + bool validate_skip(vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override { + return (int)cs.prefetch_ulong(2); + } + static ton::AccountIdPrefixFull get_prefix(vm::CellSlice&& cs); + static ton::AccountIdPrefixFull get_prefix(const vm::CellSlice& cs); + static ton::AccountIdPrefixFull get_prefix(Ref cs_ref); + static bool get_prefix_to(vm::CellSlice&& cs, ton::AccountIdPrefixFull& pfx) { + return (pfx = get_prefix(std::move(cs))).is_valid(); + } + static bool get_prefix_to(const vm::CellSlice& cs, ton::AccountIdPrefixFull& pfx) { + return (pfx = get_prefix(cs)).is_valid(); + } + static bool get_prefix_to(Ref cs_ref, ton::AccountIdPrefixFull& pfx) { + return cs_ref.not_null() && (pfx = get_prefix(std::move(cs_ref))).is_valid(); + } + bool skip_get_depth(vm::CellSlice& cs, int& depth) const; + bool extract_std_address(Ref cs_ref, ton::WorkchainId& workchain, ton::StdSmcAddress& addr, + bool rewrite = true) const; + bool extract_std_address(vm::CellSlice& cs, ton::WorkchainId& workchain, ton::StdSmcAddress& addr, + bool rewrite = true) const; +}; + +extern const MsgAddressInt t_MsgAddressInt; + +struct MsgAddressExt final : TLB { + enum { addr_none = 0, addr_ext = 1 }; + int get_size(const vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override { + return (int)cs.prefetch_ulong(2); + } +}; + +extern const MsgAddressExt t_MsgAddressExt; + +struct MsgAddress final : TLB_Complex { + enum { addr_none = 0, addr_ext = 1, addr_std = 2, addr_var = 3 }; + bool validate_skip(vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override { + return (int)cs.prefetch_ulong(2); + } +}; + +extern const MsgAddress t_MsgAddress; + +struct ExtraCurrencyCollection final : TLB { + HashmapE dict_type; + ExtraCurrencyCollection() : dict_type(32, t_VarUIntegerPos_32) { + } + int get_size(const vm::CellSlice& cs) const override { + return dict_type.get_size(cs); + } + bool validate(const vm::CellSlice& cs) const override { + return dict_type.validate(cs); + } + bool null_value(vm::CellBuilder& cb) const override { + return cb.store_zeroes_bool(1); + } + bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override { + return dict_type.add_values(cb, cs1, cs2); + } + int sub_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override { + return dict_type.sub_values(cb, cs1, cs2); + } + bool add_values_ref(Ref& res, Ref arg1, Ref arg2) const { + return dict_type.add_values_ref(res, std::move(arg1), std::move(arg2)); + } + int sub_values_ref(Ref& res, Ref arg1, Ref arg2) const { + return dict_type.sub_values_ref(res, std::move(arg1), std::move(arg2)); + } + bool store_ref(vm::CellBuilder& cb, Ref arg) const { + return dict_type.store_ref(cb, std::move(arg)); + } + unsigned precompute_size(Ref arg) const { + return arg.is_null() ? 1 : 0x10001; + } +}; + +extern const ExtraCurrencyCollection t_ExtraCurrencyCollection; + +struct CurrencyCollection final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; + bool null_value(vm::CellBuilder& cb) const override { + return cb.store_bits_same_bool(1 + 4, false); + } + bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override; + bool unpack_special(vm::CellSlice& cs, td::RefInt256& balance, Ref& extra) const; + bool pack_special(vm::CellBuilder& cb, td::RefInt256 balance, Ref extra) const; + bool unpack(vm::CellSlice& cs, block::CurrencyCollection& res) const; + bool pack(vm::CellBuilder& cb, const block::CurrencyCollection& res) const; + unsigned precompute_size(td::RefInt256 balance, Ref extra) const { + return t_Grams.precompute_size(std::move(balance)) + t_ExtraCurrencyCollection.precompute_size(std::move(extra)); + } +}; + +extern const CurrencyCollection t_CurrencyCollection; + +struct CommonMsgInfo final : TLB_Complex { + enum { int_msg_info = 0, ext_in_msg_info = 2, ext_out_msg_info = 3 }; + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override { + int v = (int)cs.prefetch_ulong(2); + return v == 1 ? int_msg_info : v; + } + struct Record_int_msg_info; + bool unpack(vm::CellSlice& cs, Record_int_msg_info& data) const; + bool get_created_lt(vm::CellSlice& cs, unsigned long long& created_lt) const; + bool is_internal(const vm::CellSlice& cs) const { + return get_tag(cs) == int_msg_info; + } +}; + +struct CommonMsgInfo::Record_int_msg_info { + bool ihr_disabled, bounce, bounced; + Ref src, dest, value, ihr_fee, fwd_fee; + unsigned long long created_lt; + unsigned created_at; +}; + +extern const CommonMsgInfo t_CommonMsgInfo; + +struct TickTock final : TLB { + int get_size(const vm::CellSlice& cs) const override { + return 2; + } +}; + +extern const TickTock t_TickTock; + +struct StateInit final : TLB_Complex { + bool validate_skip(vm::CellSlice& cs) const override; + bool get_ticktock(vm::CellSlice& cs, int& ticktock) const; +}; + +extern const StateInit t_StateInit; + +struct Message final : TLB_Complex { + bool validate_skip(vm::CellSlice& cs) const override; + bool extract_info(vm::CellSlice& cs) const; + bool get_created_lt(vm::CellSlice& cs, unsigned long long& created_lt) const; + bool is_internal(const vm::CellSlice& cs) const { + return t_CommonMsgInfo.is_internal(cs); + } + bool is_internal(Ref ref) const; +}; + +extern const Message t_Message; +extern const RefTo t_Ref_Message; + +struct IntermediateAddress final : TLB_Complex { + enum { interm_addr_regular = 0, interm_addr_simple = 2, interm_addr_ext = 3 }; + int get_size(const vm::CellSlice& cs) const override; + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + bool fetch_regular(vm::CellSlice& cs, int& use_dst_bits) const { + return cs.fetch_uint_to(8, use_dst_bits) && use_dst_bits <= 96; + } + int get_tag(const vm::CellSlice& cs) const override { + int v = (int)cs.prefetch_ulong(2); + return v == 1 ? interm_addr_regular : v; + } +}; + +extern const IntermediateAddress t_IntermediateAddress; + +struct MsgEnvelope final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + bool extract_fwd_fees_remaining(vm::CellSlice& cs) const; + struct Record { + typedef MsgEnvelope type_class; + Ref cur_addr, next_addr, fwd_fee_remaining; + Ref msg; + }; + struct Record_std { + typedef MsgEnvelope type_class; + int cur_addr, next_addr; + td::RefInt256 fwd_fee_remaining; + Ref msg; + }; + bool unpack(vm::CellSlice& cs, Record& data) const; + bool unpack(vm::CellSlice& cs, Record_std& data) const; + bool unpack_std(vm::CellSlice& cs, int& cur_a, int& nhop_a, Ref& msg) const; + bool get_created_lt(const vm::CellSlice& cs, unsigned long long& created_lt) const; +}; + +extern const MsgEnvelope t_MsgEnvelope; +extern const RefTo t_Ref_MsgEnvelope; + +struct StorageUsed final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; +}; + +extern const StorageUsed t_StorageUsed; + +struct StorageUsedShort final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; +}; + +extern const StorageUsedShort t_StorageUsedShort; + +struct StorageInfo final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; +}; + +extern const StorageInfo t_StorageInfo; + +struct AccountState final : TLB_Complex { + enum { account_uninit = 0, account_frozen = 1, account_active = 2 }; + bool validate_skip(vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override { + int t = (int)cs.prefetch_ulong(2); + return t == 3 ? account_active : t; + } + bool get_ticktock(vm::CellSlice& cs, int& ticktock) const; +}; + +extern const AccountState t_AccountState; + +struct AccountStorage final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + bool skip_copy_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const; +}; + +extern const AccountStorage t_AccountStorage; + +struct Account final : TLB_Complex { + enum { account_none = 0, account = 1 }; + bool allow_empty; + Account(bool _allow_empty = false) : allow_empty(_allow_empty) { + } + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + // Ref get_balance(const vm::CellSlice& cs) const; + bool skip_copy_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const; + bool skip_copy_depth_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const; + int get_tag(const vm::CellSlice& cs) const override { + return (int)cs.prefetch_ulong(1); + } +}; + +extern const Account t_Account, t_AccountE; +extern const RefTo t_Ref_Account; + +struct AccountStatus final : TLB { + enum { acc_state_uninit, acc_state_frozen, acc_state_active, acc_state_nonexist }; + int get_size(const vm::CellSlice& cs) const override { + return 2; + } + int get_tag(const vm::CellSlice& cs) const override { + return (int)cs.prefetch_ulong(2); + } +}; + +extern const AccountStatus t_AccountStatus; + +struct ShardAccount final : TLB_Complex { + struct Record { + using Type = ShardAccount; + Ref account; + ton::LogicalTime last_trans_lt; + ton::Bits256 last_trans_hash; + bool valid{false}; + bool is_zero{false}; + bool reset(); + bool unpack(vm::CellSlice& cs); + bool unpack(Ref cs_ref); + bool invalidate() { + return valid = false; + } + }; + int get_size(const vm::CellSlice& cs) const override { + return 0x10140; + } + bool skip(vm::CellSlice& cs) const override { + return cs.advance_ext(0x140, 1); + } + bool validate_skip(vm::CellSlice& cs) const override { + return cs.advance(0x140) && t_Ref_Account.validate_skip(cs); + } + static bool unpack(vm::CellSlice& cs, Record& info) { + return info.unpack(cs); + } + static bool unpack(Ref cs_ref, Record& info) { + return info.unpack(std::move(cs_ref)); + } + static bool extract_account_state(Ref cs_ref, Ref& acc_state); +}; + +extern const ShardAccount t_ShardAccount; + +struct DepthBalanceInfo final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + bool null_value(vm::CellBuilder& cb) const override; + bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override; +}; + +extern const DepthBalanceInfo t_DepthBalanceInfo; + +struct Aug_ShardAccounts final : AugmentationCheckData { + Aug_ShardAccounts() : AugmentationCheckData(t_ShardAccount, t_DepthBalanceInfo) { + } + bool eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const override; +}; + +extern const Aug_ShardAccounts aug_ShardAccounts; + +struct ShardAccounts final : TLB_Complex { + HashmapAugE dict_type; + ShardAccounts() : dict_type(256, aug_ShardAccounts){}; + bool skip(vm::CellSlice& cs) const override { + return dict_type.skip(cs); + } + bool validate_skip(vm::CellSlice& cs) const override { + return dict_type.validate_skip(cs); + } +}; + +extern const ShardAccounts t_ShardAccounts; + +struct AccStatusChange final : TLB { + enum { acst_unchanged = 0, acst_frozen = 2, acst_deleted = 3 }; + int get_size(const vm::CellSlice& cs) const override { + return cs.prefetch_ulong(1) ? 2 : 1; + } + int get_tag(const vm::CellSlice& cs) const override { + if (cs.size() == 1) { + return (int)cs.prefetch_ulong(1) ? -1 : acst_unchanged; + } + int v = (int)cs.prefetch_ulong(2); + return v == 1 ? acst_unchanged : v; + } +}; + +extern const AccStatusChange t_AccStatusChange; + +struct TrStoragePhase final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; +}; + +extern const TrStoragePhase t_TrStoragePhase; + +struct TrCreditPhase final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; +}; + +extern const TrCreditPhase t_TrCreditPhase; + +struct TrComputeInternal1 final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; +}; + +struct ComputeSkipReason final : TLB { + enum { cskip_no_state = 0, cskip_bad_state = 1, cskip_no_gas = 2 }; + int get_size(const vm::CellSlice& cs) const override { + return 2; + } + bool validate_skip(vm::CellSlice& cs) const override { + return get_tag(cs) >= 0 && cs.advance(2); + } + int get_tag(const vm::CellSlice& cs) const override { + int t = (int)cs.prefetch_ulong(2); + return t < 3 ? t : -1; + } +}; + +extern const ComputeSkipReason t_ComputeSkipReason; + +struct TrComputePhase final : TLB_Complex { + enum { tr_phase_compute_skipped = 0, tr_phase_compute_vm = 1 }; + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override { + return (int)cs.prefetch_ulong(1); + } +}; + +extern const TrComputePhase t_TrComputePhase; + +struct TrActionPhase final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; +}; + +extern const TrActionPhase t_TrActionPhase; + +struct TrBouncePhase final : TLB_Complex { + enum { tr_phase_bounce_negfunds = 0, tr_phase_bounce_nofunds = 1, tr_phase_bounce_ok = 2 }; + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override; +}; + +extern const TrBouncePhase t_TrBouncePhase; + +struct SplitMergeInfo final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; +}; + +extern const SplitMergeInfo t_SplitMergeInfo; + +struct TransactionDescr final : TLB_Complex { + enum { + trans_ord = 0, + trans_storage = 1, + trans_tick_tock = 2, + trans_split_prepare = 4, + trans_split_install = 5, + trans_merge_prepare = 6, + trans_merge_install = 7 + }; + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override; +}; + +extern const TransactionDescr t_TransactionDescr; + +struct Transaction final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + bool get_total_fees(vm::CellSlice&& cs, td::RefInt256& total_fees) const; +}; + +extern const Transaction t_Transaction; +extern const RefTo t_Ref_Transaction; + +struct Aug_AccountTransactions final : AugmentationCheckData { + Aug_AccountTransactions() : AugmentationCheckData(t_Ref_Transaction, t_Grams) { + } + bool eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const override; +}; + +extern const Aug_AccountTransactions aug_AccountTransactions; +extern const HashmapAug t_AccountTransactions; // (HashmapAug 64 ^Transaction Grams) + +struct HashUpdate final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override { + return cs.advance(8 + 256 * 2); + } + bool validate_skip(vm::CellSlice& cs) const override { + return cs.fetch_ulong(8) == 0x72 && cs.advance(256 * 2); + } +}; + +extern const HashUpdate t_HashUpdate; +extern const RefTo t_Ref_HashUpdate; + +struct AccountBlock final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + bool get_total_fees(vm::CellSlice&& cs, td::RefInt256& total_fees) const; +}; + +extern const AccountBlock t_AccountBlock; + +struct Aug_ShardAccountBlocks final : AugmentationCheckData { + Aug_ShardAccountBlocks() : AugmentationCheckData(t_AccountBlock, t_Grams) { + } + bool eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const override; +}; + +extern const Aug_ShardAccountBlocks aug_ShardAccountBlocks; +extern const HashmapAugE t_ShardAccountBlocks; // (HashmapAugE 256 AccountBlock Grams) + +struct ImportFees final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + bool null_value(vm::CellBuilder& cb) const override { + return cb.store_bits_same_bool(4 + 4 + 1, false); + } + bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override; +}; + +extern const ImportFees t_ImportFees; + +struct InMsg final : TLB_Complex { + enum { + msg_import_ext = 0, + msg_import_ihr = 2, + msg_import_imm = 3, + msg_import_fin = 4, + msg_import_tr = 5, + msg_discard_fin = 6, + msg_discard_tr = 7 + }; + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override { + return (int)cs.prefetch_ulong(3); + } + bool get_import_fees(vm::CellBuilder& cb, vm::CellSlice& cs) const; +}; + +extern const InMsg t_InMsg; + +struct OutMsg final : TLB_Complex { + enum { + msg_export_ext = 0, + msg_export_new = 1, + msg_export_imm = 2, + msg_export_tr = 3, + msg_export_deq_imm = 4, + msg_export_deq = 6, + msg_export_tr_req = 7 + }; + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override { + return (int)cs.prefetch_ulong(3); + } + bool get_export_value(vm::CellBuilder& cb, vm::CellSlice& cs) const; + bool get_created_lt(vm::CellSlice& cs, unsigned long long& created_lt) const; +}; + +extern const OutMsg t_OutMsg; + +// next: InMsgDescr, OutMsgDescr, OutMsgQueue, and their augmentations + +struct Aug_InMsgDescr final : AugmentationCheckData { + Aug_InMsgDescr() : AugmentationCheckData(t_InMsg, t_ImportFees) { + } + bool eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const override { + return t_InMsg.get_import_fees(cb, cs); + } +}; + +extern const Aug_InMsgDescr aug_InMsgDescr; + +struct InMsgDescr final : TLB_Complex { + HashmapAugE dict_type; + InMsgDescr() : dict_type(256, aug_InMsgDescr){}; + bool skip(vm::CellSlice& cs) const override { + return dict_type.skip(cs); + } + bool validate_skip(vm::CellSlice& cs) const override { + return dict_type.validate_skip(cs); + } +}; + +extern const InMsgDescr t_InMsgDescr; + +struct Aug_OutMsgDescr final : AugmentationCheckData { + Aug_OutMsgDescr() : AugmentationCheckData(t_OutMsg, t_CurrencyCollection) { + } + bool eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const override { + return t_OutMsg.get_export_value(cb, cs); + } +}; + +extern const Aug_OutMsgDescr aug_OutMsgDescr; + +struct OutMsgDescr final : TLB_Complex { + HashmapAugE dict_type; + OutMsgDescr() : dict_type(256, aug_OutMsgDescr){}; + bool skip(vm::CellSlice& cs) const override { + return dict_type.skip(cs); + } + bool validate_skip(vm::CellSlice& cs) const override { + return dict_type.validate_skip(cs); + } +}; + +extern const OutMsgDescr t_OutMsgDescr; + +struct EnqueuedMsg final : TLB_Complex { + int get_size(const vm::CellSlice& cs) const override { + return 0x10040; + } + bool skip(vm::CellSlice& cs) const override { + return cs.advance_ext(0x10040); + } + bool validate_skip(vm::CellSlice& cs) const override; + bool unpack(vm::CellSlice& cs, EnqueuedMsgDescr& descr) const { + return descr.unpack(cs); + } +}; + +extern const EnqueuedMsg t_EnqueuedMsg; + +struct Aug_OutMsgQueue final : AugmentationCheckData { + Aug_OutMsgQueue() : AugmentationCheckData(t_EnqueuedMsg, t_uint64) { + } + bool eval_fork(vm::CellBuilder& cb, vm::CellSlice& left_cs, vm::CellSlice& right_cs) const override; + bool eval_empty(vm::CellBuilder& cb) const override; + bool eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const override; +}; + +extern const Aug_OutMsgQueue aug_OutMsgQueue; + +struct OutMsgQueue final : TLB_Complex { + HashmapAugE dict_type; + OutMsgQueue() : dict_type(32 + 64 + 256, aug_OutMsgQueue){}; + bool skip(vm::CellSlice& cs) const override { + return dict_type.skip(cs); + } + bool validate_skip(vm::CellSlice& cs) const override { + return dict_type.validate_skip(cs); + } +}; + +extern const OutMsgQueue t_OutMsgQueue; + +struct ProcessedUpto final : TLB { + int get_size(const vm::CellSlice& cs) const override { + return 64 + 256; + } +}; + +extern const ProcessedUpto t_ProcessedUpto; +extern const HashmapE t_ProcessedInfo; +extern const HashmapE t_IhrPendingInfo; + +struct OutMsgQueueInfo final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; +}; + +extern const OutMsgQueueInfo t_OutMsgQueueInfo; +extern const RefTo t_Ref_OutMsgQueueInfo; + +struct ExtBlkRef final : TLB { + enum { fixed_size = 64 + 32 + 256 * 2 }; + int get_size(const vm::CellSlice& cs) const override { + return fixed_size; + } + bool unpack(vm::CellSlice& cs, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const; + bool unpack(Ref cs_ref, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const; +}; + +extern const ExtBlkRef t_ExtBlkRef; + +struct BlkMasterInfo final : TLB { + int get_size(const vm::CellSlice& cs) const override { + return t_ExtBlkRef.get_size(cs); + } +}; + +extern const BlkMasterInfo t_BlkMasterInfo; + +struct ShardIdent final : TLB_Complex { + struct Record; + int get_size(const vm::CellSlice& cs) const override { + return 2 + 6 + 32 + 64; + } + bool skip(vm::CellSlice& cs) const override { + return cs.advance(get_size(cs)); + } + bool validate_skip(vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override { + return 0; + } + bool unpack(vm::CellSlice& cs, Record& data) const; + bool pack(vm::CellBuilder& cb, const Record& data) const; + bool unpack(vm::CellSlice& cs, ton::ShardIdFull& data) const; + bool pack(vm::CellBuilder& cb, ton::ShardIdFull data) const; + bool unpack(vm::CellSlice& cs, ton::WorkchainId& workchain, ton::ShardId& shard) const; + bool pack(vm::CellBuilder& cb, ton::WorkchainId workchain, ton::ShardId shard) const; +}; + +struct ShardIdent::Record { + int shard_pfx_bits; + int workchain_id; + unsigned long long shard_prefix; + Record() : shard_pfx_bits(-1) { + } + Record(int _pfxlen, int _wcid, unsigned long long _pfx) + : shard_pfx_bits(_pfxlen), workchain_id(_wcid), shard_prefix(_pfx) { + } + bool check() const; + bool is_valid() const { + return shard_pfx_bits >= 0; + } + void invalidate() { + shard_pfx_bits = -1; + } +}; + +extern const ShardIdent t_ShardIdent; + +struct BlockIdExt final : TLB_Complex { + int get_size(const vm::CellSlice& cs) const override { + return 2 + 6 + 32 + 64 + 32 + 256 * 2; + } + bool skip(vm::CellSlice& cs) const override { + return cs.advance(get_size(cs)); + } + bool validate_skip(vm::CellSlice& cs) const override; + bool unpack(vm::CellSlice& cs, ton::BlockIdExt& data) const; + bool pack(vm::CellBuilder& cb, const ton::BlockIdExt& data) const; +}; + +extern const BlockIdExt t_BlockIdExt; + +struct ShardState final : TLB_Complex { + enum { shard_state = (int)0x9023afe1, split_state = 0x5f327da5 }; + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override { + return (int)cs.prefetch_ulong(32) == shard_state ? shard_state : -1; + } +}; + +extern const ShardState t_ShardState; + +struct ShardState_aux final : TLB_Complex { + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override { + return 0; + } +}; + +extern const ShardState_aux t_ShardState_aux; + +struct LibDescr final : TLB_Complex { + enum { shared_lib_descr = 0 }; + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; + int get_tag(const vm::CellSlice& cs) const override { + return (int)cs.prefetch_ulong(2); + } +}; + +extern const LibDescr t_LibDescr; + +struct BlkPrevInfo final : TLB_Complex { + bool merged; + BlkPrevInfo(bool _merged) : merged(_merged) { + } + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; +}; + +extern const BlkPrevInfo t_BlkPrevInfo_0; + +struct McStateExtra final : TLB_Complex { + enum { masterchain_state_extra = 0xcc21 }; + bool skip(vm::CellSlice& cs) const override; + bool validate_skip(vm::CellSlice& cs) const override; +}; + +extern const McStateExtra t_McStateExtra; + +struct KeyExtBlkRef final : TLB { + enum { fixed_size = 1 + ExtBlkRef::fixed_size }; + int get_size(const vm::CellSlice& cs) const override { + return fixed_size; + } +}; + +extern const KeyExtBlkRef t_KeyExtBlkRef; + +struct KeyMaxLt final : TLB { + enum { fixed_size = 1 + 64 }; + int get_size(const vm::CellSlice& cs) const override { + return fixed_size; + } + bool null_value(vm::CellBuilder& cb) const override { + return cb.store_bits_same_bool(fixed_size, false); + } + bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override; +}; + +extern const KeyMaxLt t_KeyMaxLt; + +struct Aug_OldMcBlocksInfo final : AugmentationCheckData { + Aug_OldMcBlocksInfo() : AugmentationCheckData(t_KeyExtBlkRef, t_KeyMaxLt) { + } + bool eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const override; +}; + +extern const Aug_OldMcBlocksInfo aug_OldMcBlocksInfo; + +} // namespace tlb +} // namespace block diff --git a/ton-test-liteclient-full/lite-client/crypto/block/block.cpp b/ton-test-liteclient-full/lite-client/crypto/block/block.cpp index 1f6d9ae..e4e9bca 100644 --- a/ton-test-liteclient-full/lite-client/crypto/block/block.cpp +++ b/ton-test-liteclient-full/lite-client/crypto/block/block.cpp @@ -1,6 +1,7 @@ #include "td/utils/bits.h" #include "block/block.h" #include "block/block-auto.h" +#include "block/block-parse.h" #include "ton/ton-shard.h" #include "common/util.h" #include "td/utils/crypto.h" @@ -8,27 +9,6 @@ namespace block { using namespace std::literals::string_literals; -using CombineError = vm::CombineError; - -bool debug(const char* str) { - std::cerr << str; - return true; -} - -bool debug(int x) { - if (x < 100) { - std::cerr << '[' << (char)(64 + x) << ']'; - } else { - std::cerr << '[' << (char)(64 + x / 100) << x % 100 << ']'; - } - return true; -} - -#define DBG_START int dbg = 0; -#define DBG debug(++dbg)&& -#define DEB_START DBG_START -#define DEB DBG - bool pack_std_smc_addr_to(char result[48], bool base64_url, ton::WorkchainId wc, const ton::StdSmcAddress& addr, bool bounceable, bool testnet) { if (wc < -128 || wc >= 128) { @@ -329,8 +309,8 @@ bool MsgProcessedUpto::contains(ton::ShardId other_shard, ton::LogicalTime other (last_inmsg_lt > other_lt || (last_inmsg_lt == other_lt && !(last_inmsg_hash < other_hash))); } -bool MsgProcessedUptoCollection::insert(ton::LogicalTime last_proc_lt, td::ConstBitPtr last_proc_hash, - ton::BlockSeqno mc_seqno) { +bool MsgProcessedUptoCollection::insert(ton::BlockSeqno mc_seqno, ton::LogicalTime last_proc_lt, + td::ConstBitPtr last_proc_hash) { if (!last_proc_lt) { return false; } @@ -343,8 +323,66 @@ bool MsgProcessedUptoCollection::insert(ton::LogicalTime last_proc_lt, td::Const return true; } -bool MsgProcessedUptoCollection::insert_infty(ton::BlockSeqno mc_seqno) { - return insert(~0ULL, td::Bits256::ones().bits(), mc_seqno); +bool MsgProcessedUptoCollection::insert_infty(ton::BlockSeqno mc_seqno, ton::LogicalTime last_proc_lt) { + return insert(mc_seqno, last_proc_lt, td::Bits256::ones().bits()); +} + +bool MsgProcessedUptoCollection::is_reduced() const { + if (!valid) { + return false; + } + for (auto it = list.begin(); it < list.end(); ++it) { + for (auto it2 = it + 1; it2 < list.end(); ++it2) { + if (it->contains(*it2) || it2->contains(*it)) { + return false; + } + } + } + return true; +} + +bool MsgProcessedUptoCollection::contains(const MsgProcessedUpto& p_upto) const { + for (const auto& z : list) { + if (z.contains(p_upto)) { + return true; + } + } + return false; +} + +bool MsgProcessedUptoCollection::contains(const MsgProcessedUptoCollection& other) const { + for (const auto& w : other.list) { + if (!contains(w)) { + return false; + } + } + return true; +} + +const MsgProcessedUpto* MsgProcessedUptoCollection::is_simple_update_of(const MsgProcessedUptoCollection& other, + bool& ok) const { + ok = false; + if (!contains(other)) { + LOG(DEBUG) << "does not cointain the previous value"; + return nullptr; + } + if (other.contains(*this)) { + LOG(DEBUG) << "coincides with the previous value"; + ok = true; + return nullptr; + } + const MsgProcessedUpto* found = nullptr; + for (const auto& z : list) { + if (!other.contains(z)) { + if (found) { + LOG(DEBUG) << "has more than two new entries"; + return found; // ok = false: update is not simple + } + found = &z; + } + } + ok = true; + return found; } ton::BlockSeqno MsgProcessedUptoCollection::min_mc_seqno() const { @@ -462,6 +500,15 @@ bool MsgProcessedUptoCollection::already_processed(const EnqueuedMsgDescr& msg) return false; } +bool MsgProcessedUptoCollection::for_each_mcseqno(std::function func) const { + for (const auto& entry : list) { + if (!func(entry.mc_seqno)) { + return false; + } + } + return true; +} + // unpacks some fields from EnqueuedMsg bool EnqueuedMsgDescr::unpack(vm::CellSlice& cs) { block::gen::EnqueuedMsg::Record enq; @@ -570,2059 +617,749 @@ bool BlockLimitStatus::would_fit(unsigned cls, ton::LogicalTime end_lt, td::uint limits.bytes.fits(cls, estimate_block_size(extra))); } -namespace tlb { - -using namespace ::tlb; - -int MsgAddressExt::get_size(const vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case addr_none: // 00, addr_none - return 2; - case addr_ext: // 01, addr_extern - if (cs.have(2 + 9)) { - int len = cs.prefetch_long(2 + 9) & 0x1ff; - return 2 + 9 + len; - } +// SETS: account_dict, shard_libraries_, mc_state_extra +// total_balance{,_extra}, total_validator_fees +// SETS: out_msg_queue, processed_upto_, ihr_pending (via unpack_out_msg_queue_info) +// SETS: utime_, lt_ +td::Status ShardState::unpack_state(ton::BlockIdExt blkid, Ref prev_state_root) { + if (!blkid.is_valid()) { + return td::Status::Error(-666, "invalid block id supplied to ShardState::unpack"); + } + if (prev_state_root.is_null()) { + return td::Status::Error(-666, "the root cell supplied for the shardchain state "s + blkid.to_str() + " is null"); + } + block::gen::ShardStateUnsplit::Record state; + if (!tlb::unpack_cell(prev_state_root, state)) { + return td::Status::Error(-666, "cannot unpack header of shardchain state "s + blkid.to_str()); + } + if ((unsigned)state.seq_no != blkid.seqno()) { + return td::Status::Error( + -666, PSTRING() << "shardchain state for " << blkid.to_str() << " has incorrect seqno " << state.seq_no); + } + auto shard1 = ton::ShardIdFull(block::ShardId{state.shard_id}); + if (shard1 != blkid.shard_full()) { + return td::Status::Error(-666, "shardchain state for "s + blkid.to_str() + + " corresponds to incorrect workchain or shard " + shard1.to_str()); + } + if (state.vert_seq_no) { + return td::Status::Error( + -666, "shardchain state for "s + blkid.to_str() + " has non-zero vert_seq_no, which is unsupported"); + } + id_ = blkid; + root_ = std::move(prev_state_root); + before_split_ = state.before_split; + account_dict_ = + std::make_unique(std::move(state.accounts), 256, block::tlb::aug_ShardAccounts); + // check that all keys in account_dict have correct prefixes + td::BitArray<64> acc_pfx{(long long)shard1.shard}; + int acc_pfx_len = shard_prefix_length(shard1); + if (!account_dict_->has_common_prefix(acc_pfx.bits(), acc_pfx_len)) { + return td::Status::Error(-666, "account dictionary of previous state of "s + id_.to_str() + " does not have " + + acc_pfx.bits().to_hex(acc_pfx_len) + " as common key prefix"); + } + // get overload / underload history + overload_history_ = state.r1.overload_history; + underload_history_ = state.r1.underload_history; + // get shard libraries + shard_libraries_ = std::make_unique(state.r1.libraries->prefetch_ref(), 256); + if (!shard_libraries_->is_empty() && !shard1.is_masterchain()) { + return td::Status::Error(-666, + "shardchain state "s + id_.to_str() + + " has a non-trivial shard libraries collection, but it is not in the masterchain"); + } + mc_state_extra_ = state.custom->prefetch_ref(); + vm::CellSlice cs{*state.r1.master_ref}; // master_ref:(Maybe BlkMasterInfo) + if ((int)cs.fetch_ulong(1) == 1) { + block::gen::ExtBlkRef::Record mc_blk; + if (!tlb::unpack_exact(cs, mc_blk)) { + return td::Status::Error(-666, "cannot unpack master_ref in shardchain state of "s + id_.to_str()); + } + mc_blk_seqno_ = mc_blk.seq_no; + } else { + mc_blk_seqno_ = 0; + } + global_id_ = state.global_id; + utime_ = state.gen_utime; + lt_ = state.gen_lt; + if (!block::tlb::t_CurrencyCollection.unpack_special(state.r1.total_balance.write(), total_balance_, + total_balance_extra_)) { + return td::Status::Error( + -666, "cannot unpack total_balance:CurrencyCollection from previous ShardState of "s + id_.to_str()); + } + auto accounts_extra = account_dict_->get_root_extra(); + td::RefInt256 old_total_balance; + Ref old_total_balance_extra; + if (!(accounts_extra.write().advance(5) && block::tlb::t_CurrencyCollection.unpack_special( + accounts_extra.write(), old_total_balance, old_total_balance_extra))) { + return td::Status::Error( + -666, + "cannot extract total account balance from ShardAccounts contained in previous ShardState of "s + id_.to_str()); + } + if (td::cmp(old_total_balance, total_balance_) != 0 || + old_total_balance_extra.not_null() != total_balance_extra_.not_null() || + (total_balance_extra_.not_null() && total_balance_extra_->get_hash() != old_total_balance_extra->get_hash())) { + return td::Status::Error(-666, + "invalid previous ShardState for "s + id_.to_str() + + ": declared total balance differs from one obtained by summing over all Accounts"); + } + Ref total_fees_extra; + if (!block::tlb::t_CurrencyCollection.unpack_special(state.r1.total_validator_fees.write(), total_validator_fees_, + total_fees_extra) || + total_fees_extra.not_null()) { + return td::Status::Error( + -666, "cannot unpack total_validator_fees:CurrencyCollection from previous ShardState of "s + id_.to_str()); + } + return unpack_out_msg_queue_info(std::move(state.out_msg_queue_info)); +} + +// SETS: out_msg_queue, processed_upto_, ihr_pending +td::Status ShardState::unpack_out_msg_queue_info(Ref out_msg_queue_info) { + block::gen::OutMsgQueueInfo::Record qinfo; + if (!tlb::unpack_cell(std::move(out_msg_queue_info), qinfo)) { + return td::Status::Error(-666, "cannot unpack OutMsgQueueInfo in the state of "s + id_.to_str()); + } + out_msg_queue_ = + std::make_unique(std::move(qinfo.out_queue), 352, block::tlb::aug_OutMsgQueue); + if (verbosity >= 3 * 0) { + LOG(DEBUG) << "unpacking ProcessedUpto of our previous block " << id_.to_str(); + block::gen::t_ProcessedInfo.print(std::cerr, qinfo.proc_info); + } + if (!block::gen::t_ProcessedInfo.validate_csr(qinfo.proc_info)) { + return td::Status::Error( + -666, "ProcessedInfo in the state of "s + id_.to_str() + " is invalid according to automated validity checks"); + } + if (!block::gen::t_IhrPendingInfo.validate_csr(qinfo.ihr_pending)) { + return td::Status::Error( + -666, "IhrPendingInfo in the state of "s + id_.to_str() + " is invalid according to automated validity checks"); + } + processed_upto_ = block::MsgProcessedUptoCollection::unpack(ton::ShardIdFull(id_), std::move(qinfo).proc_info); + ihr_pending_ = std::make_unique(std::move(qinfo.ihr_pending), 320); + auto shard1 = id_.shard_full(); + td::BitArray<64> pfx{(long long)shard1.shard}; + int pfx_len = shard_prefix_length(shard1); + if (!ihr_pending_->has_common_prefix(pfx.bits(), pfx_len)) { + return td::Status::Error(-666, "IhrPendingInfo in the state of "s + id_.to_str() + " does not have " + + pfx.bits().to_hex(pfx_len) + " as common key prefix"); } - return -1; + return td::Status::OK(); } -const MsgAddressExt t_MsgAddressExt; - -const Anycast t_Anycast; - -bool Maybe_Anycast::skip_get_depth(vm::CellSlice& cs, int& depth) const { - depth = 0; - bool have; - return cs.fetch_bool_to(have) && (!have || t_Anycast.skip_get_depth(cs, depth)); +// UPDATES: prev_state_utime_, prev_state_lt_ +bool ShardState::update_prev_utime_lt(ton::UnixTime& prev_utime, ton::LogicalTime& prev_lt) const { + prev_utime = std::max(prev_utime, utime_); + prev_lt = std::max(prev_lt, lt_); + return true; } -const Maybe_Anycast t_Maybe_Anycast; - -bool MsgAddressInt::validate_skip(vm::CellSlice& cs) const { - if (!cs.have(3)) { - return false; - } - switch (get_tag(cs)) { - case addr_std: - return cs.advance(2) && t_Maybe_Anycast.skip(cs) && cs.advance(8 + 256); - case addr_var: - if (cs.advance(2) && t_Maybe_Anycast.skip(cs) && cs.have(9 + 32)) { - int addr_len = (int)cs.fetch_ulong(9); - int workchain_id = (int)cs.fetch_long(32); - return cs.advance(addr_len) && (workchain_id < -0x80 || workchain_id > 0x7f || addr_len != 256) && - (workchain_id != 0 && workchain_id != -1); - } +td::Status ShardState::check_before_split(bool req_before_split) const { + CHECK(id_.is_valid()); + if (before_split_ != req_before_split) { + return td::Status::Error(PSTRING() << "previous state for " << id_.to_str() << " has before_split=" << before_split_ + << ", but we have after_split=" << req_before_split); } - return false; + return td::Status::OK(); } -bool MsgAddressInt::skip_get_depth(vm::CellSlice& cs, int& depth) const { - if (!cs.have(3)) { - return false; - } - switch (get_tag(cs)) { - case addr_std: - return cs.advance(2) && t_Maybe_Anycast.skip_get_depth(cs, depth) && cs.advance(8 + 256); - case addr_var: - if (cs.advance(2) && t_Maybe_Anycast.skip_get_depth(cs, depth) && cs.have(9 + 32)) { - int addr_len = (int)cs.fetch_ulong(9); - return cs.advance(32 + addr_len); - } +td::Status ShardState::check_global_id(int req_global_id) const { + if (global_id_ != req_global_id) { + return td::Status::Error(-666, PSTRING() << "global blockchain id mismatch in shard state of " << id_.to_str() + << ": expected " << req_global_id << ", found " << global_id_); } - return false; + return td::Status::OK(); } -ton::AccountIdPrefixFull MsgAddressInt::get_prefix(vm::CellSlice&& cs) const { - if (!cs.have(3 + 8 + 64)) { - return {}; - } - ton::WorkchainId workchain; - unsigned long long prefix; - int t = (int)cs.prefetch_ulong(2 + 1 + 5); - switch (t >> 5) { - case 4: { // addr_std$10, anycast=nothing$0 - if (cs.advance(3) && cs.fetch_int_to(8, workchain) && cs.fetch_uint_to(64, prefix)) { - return {workchain, prefix}; - } - break; - } - case 5: { // addr_std$10, anycast=just$1 (Anycast) - t &= 31; // depth:(## 5) - unsigned long long rewrite; - if (cs.advance(8) && cs.fetch_uint_to(t, rewrite) // rewrite_pfx:(bits depth) - && cs.fetch_int_to(8, workchain) // workchain_id:int8 - && cs.fetch_uint_to(64, prefix)) { // address:bits256 - rewrite <<= 64 - t; - return {workchain, (prefix & (std::numeric_limits::max() >> t)) | rewrite}; - } - break; - } - case 6: { // addr_var$11, anycast=nothing$0 - int len; - if (cs.advance(3) && cs.fetch_uint_to(9, len) // addr_len:(## 9) - && len >= 64 // { len >= 64 } - && cs.fetch_int_to(32, workchain) // workchain_id:int32 - && cs.fetch_uint_to(64, prefix)) { // address:(bits addr_len) - return {workchain, prefix}; - } - break; - } - case 7: { // addr_var$11, anycast=just$1 (Anycast) - t &= 31; // depth:(## 5) - int len; - unsigned long long rewrite; - if (cs.advance(8) && cs.fetch_uint_to(t, rewrite) // rewrite_pfx:(bits depth) - && cs.fetch_uint_to(9, len) // addr_len:(## 9) - && len >= 64 // { len >= 64 } - && cs.fetch_int_to(32, workchain) // workchain_id:int32 - && cs.fetch_uint_to(64, prefix)) { // address:bits256 - rewrite <<= 64 - t; - return {workchain, (prefix & (std::numeric_limits::max() >> t)) | rewrite}; - } - break; - } +td::Status ShardState::check_mc_blk_seqno(ton::BlockSeqno last_mc_block_seqno) const { + if (mc_blk_seqno_ > last_mc_block_seqno) { + return td::Status::Error( + -666, PSTRING() << "previous block refers to masterchain block with seqno " << mc_blk_seqno_ + << " larger than the latest known masterchain block seqno " << last_mc_block_seqno); } - return {}; + return td::Status::OK(); } -ton::AccountIdPrefixFull MsgAddressInt::get_prefix(const vm::CellSlice& cs) const { - vm::CellSlice cs2{cs}; - return get_prefix(std::move(cs2)); +td::Status ShardState::unpack_state_ext(ton::BlockIdExt id, Ref state_root, int global_id, + ton::BlockSeqno prev_mc_block_seqno, bool after_split, bool after_merge, + std::function for_each_mcseqno_func) { + TRY_STATUS(unpack_state(id, std::move(state_root))); + TRY_STATUS(check_global_id(global_id)); + TRY_STATUS(check_mc_blk_seqno(prev_mc_block_seqno)); + TRY_STATUS(check_before_split(after_split)); + clear_load_history_if(after_split || after_merge); + if (!for_each_mcseqno(std::move(for_each_mcseqno_func))) { + return td::Status::Error( + -666, "cannot perform necessary actions for each mc_seqno mentioned in ProcessedUpto of "s + id_.to_str()); + } + return td::Status::OK(); } -ton::AccountIdPrefixFull MsgAddressInt::get_prefix(Ref cs_ref) const { - if (cs_ref->is_unique()) { - return get_prefix(std::move(cs_ref.unique_write())); - } else { - vm::CellSlice cs{*cs_ref}; - return get_prefix(std::move(cs)); - } +td::Status ShardState::merge_with(ShardState& sib) { + // 1. check that the two states are valid and belong to sibling shards + if (!is_valid() || !sib.is_valid()) { + return td::Status::Error(-666, "cannot merge invalid or uninitialized states"); + } + if (!ton::shard_is_sibling(id_.shard_full(), sib.id_.shard_full())) { + return td::Status::Error(-666, "cannot merge non-sibling states of "s + id_.to_str() + " and " + sib.id_.to_str()); + } + ton::ShardIdFull shard = ton::shard_parent(id_.shard_full()); + // 2. compute total_balance and total_validator_fees + total_balance_ += std::move(sib.total_balance_); + if (!block::add_extra_currency(std::move(total_balance_extra_), std::move(sib.total_balance_extra_), + total_balance_extra_)) { + return td::Status::Error(-667, "cannot add total_balance_extra of the two states being merged"); + } + total_validator_fees_ += std::move(sib.total_validator_fees_); + // 3. merge account_dict with sibling_account_dict + LOG(DEBUG) << "merging account dictionaries"; + if (!account_dict_->combine_with(*sib.account_dict_)) { + return td::Status::Error(-666, "cannot merge account dictionaries of the two ancestors"); + } + sib.account_dict_.reset(); + // 3.1. check that all keys in merged account_dict have correct prefixes + td::BitArray<64> pfx{(long long)shard.shard}; + int pfx_len = shard_prefix_length(shard); + if (!account_dict_->has_common_prefix(pfx.bits(), pfx_len)) { + return td::Status::Error(-666, "merged account dictionary of previous states of "s + shard.to_str() + + " does not have " + pfx.bits().to_hex(pfx_len) + " as common key prefix"); + } + // 3.2. check total balance of the new account_dict + auto accounts_extra = account_dict_->get_root_extra(); + td::RefInt256 old_total_balance; + Ref old_total_balance_extra; + if (!(accounts_extra.write().advance(5) && block::tlb::t_CurrencyCollection.unpack_special( + accounts_extra.write(), old_total_balance, old_total_balance_extra))) { + return td::Status::Error(-666, "cannot extract total account balance from merged accounts dictionary"); + } + if (td::cmp(old_total_balance, total_balance_) != 0 || + old_total_balance_extra.not_null() != total_balance_extra_.not_null() || + (total_balance_extra_.not_null() && total_balance_extra_->get_hash() != old_total_balance_extra->get_hash())) { + return td::Status::Error( + -666, + "invalid merged account dictionary: declared total balance differs from one obtained by summing over all " + "Accounts"); + } + // 4. merge shard libraries + CHECK(shard_libraries_->is_empty() && sib.shard_libraries_->is_empty()); + // 5. merge out_msg_queue + LOG(DEBUG) << "merging outbound message queues"; + if (!out_msg_queue_->combine_with(*sib.out_msg_queue_)) { + return td::Status::Error(-666, "cannot merge outbound message queues of the two ancestor states"); + } + sib.out_msg_queue_.reset(); + // 6. merge processed_upto + LOG(DEBUG) << "merging ProcessedUpto structures"; + if (!processed_upto_->combine_with(*sib.processed_upto_)) { + return td::Status::Error(-666, "cannot merge ProcessedUpto structures of the two ancestor states"); + } + sib.processed_upto_.reset(); + // 7. merge ihr_pending + LOG(DEBUG) << "merging IhrPendingInfo"; + if (!ihr_pending_->combine_with(*sib.ihr_pending_)) { + return td::Status::Error(-666, "cannot merge IhrPendingInfo of the two ancestors"); + } + sib.ihr_pending_.reset(); + // 7.1. check whether all keys of the new ihr_pending have correct prefix + if (!ihr_pending_->has_common_prefix(pfx.bits(), pfx_len)) { + return td::Status::Error(-666, "merged IhrPendingInfo of the two previous states of "s + shard.to_str() + + " does not have " + pfx.bits().to_hex(pfx_len) + " as common key prefix"); + } + // 8. compute merged utime_ and lt_ + utime_ = std::max(utime_, sib.utime_); + lt_ = std::max(lt_, sib.lt_); + // 9. compute underload & overload history + underload_history_ = overload_history_ = 0; + // Anything else? add here + // ... + + // 10. compute new root + if (!block::gen::t_ShardState.cell_pack_split_state(root_, std::move(root_), std::move(sib.root_))) { + return td::Status::Error(-667, "cannot construct a virtual split_state after a merge"); + } + // 11. invalidate sibling, change id_ to the (virtual) common parent + sib.invalidate(); + id_.id.shard = shard.shard; + id_.file_hash.set_zero(); + id_.root_hash.set_zero(); + return td::Status::OK(); } -bool MsgAddressInt::extract_std_address(Ref cs_ref, ton::WorkchainId& workchain, - ton::StdSmcAddress& addr, bool rewrite) const { - if (cs_ref.is_null()) { - return false; - } else if (cs_ref->is_unique()) { - return extract_std_address(cs_ref.unique_write(), workchain, addr, rewrite); - } else { - vm::CellSlice cs{*cs_ref}; - return extract_std_address(cs, workchain, addr, rewrite); - } +td::Result> ShardState::compute_split_out_msg_queue( + ton::ShardIdFull subshard) { + auto shard = id_.shard_full(); + if (!ton::shard_is_parent(shard, subshard)) { + return td::Status::Error(-666, "cannot split subshard "s + subshard.to_str() + " from state of " + id_.to_str() + + " because it is not a parent"); + } + CHECK(out_msg_queue_); + auto subqueue = std::make_unique(*out_msg_queue_); + int res = block::filter_out_msg_queue(*subqueue, shard, subshard); + if (res < 0) { + return td::Status::Error(-666, "error splitting OutMsgQueue of "s + id_.to_str()); + } + LOG(DEBUG) << "OutMsgQueue split counter: " << res << " messages"; + return std::move(subqueue); +} + +td::Result> ShardState::compute_split_processed_upto( + ton::ShardIdFull subshard) { + if (!ton::shard_is_parent(id_.shard_full(), subshard)) { + return td::Status::Error(-666, "cannot split subshard "s + subshard.to_str() + " from state of " + id_.to_str() + + " because it is not a parent"); + } + CHECK(processed_upto_); + auto sub_processed_upto = std::make_shared(*processed_upto_); + if (!sub_processed_upto->split(subshard)) { + return td::Status::Error(-666, "error splitting ProcessedUpto of "s + id_.to_str()); + } + return std::move(sub_processed_upto); +} + +td::Status ShardState::split(ton::ShardIdFull subshard) { + if (!ton::shard_is_parent(id_.shard_full(), subshard)) { + return td::Status::Error(-666, "cannot split subshard "s + subshard.to_str() + " from state of " + id_.to_str() + + " because it is not a parent"); + } + // Have to split: + // 1. account_dict + LOG(DEBUG) << "splitting account dictionary"; + td::BitArray<64> pfx{(long long)subshard.shard}; + int pfx_len = shard_prefix_length(subshard); + CHECK(account_dict_); + CHECK(account_dict_->cut_prefix_subdict(pfx.bits(), pfx_len)); + CHECK(account_dict_->has_common_prefix(pfx.bits(), pfx_len)); + // 2. out_msg_queue + LOG(DEBUG) << "splitting OutMsgQueue"; + auto shard1 = id_.shard_full(); + CHECK(ton::shard_is_parent(shard1, subshard)); + CHECK(out_msg_queue_); + int res1 = block::filter_out_msg_queue(*out_msg_queue_, shard1, subshard); + if (res1 < 0) { + return td::Status::Error(-666, "error splitting OutMsgQueue of "s + id_.to_str()); + } + LOG(DEBUG) << "split counters: " << res1; + // 3. processed_upto + LOG(DEBUG) << "splitting ProcessedUpto"; + CHECK(processed_upto_); + if (!processed_upto_->split(subshard)) { + return td::Status::Error(-666, "error splitting ProcessedUpto of "s + id_.to_str()); + } + // 4. ihr_pending + LOG(DEBUG) << "splitting IhrPending"; + CHECK(ihr_pending_->cut_prefix_subdict(pfx.bits(), pfx_len)); + CHECK(ihr_pending_->has_common_prefix(pfx.bits(), pfx_len)); + // 5. adjust total_balance + LOG(DEBUG) << "splitting total_balance"; + auto old_total_balance = total_balance_; + auto accounts_extra = account_dict_->get_root_extra(); + if (!(accounts_extra.write().advance(5) && block::tlb::t_CurrencyCollection.unpack_special( + accounts_extra.write(), total_balance_, total_balance_extra_))) { + LOG(ERROR) << "cannot unpack CurrencyCollection from the root of newly-split accounts dictionary"; + return td::Status::Error( + -666, "error splitting total balance in account dictionary of shardchain state "s + id_.to_str()); + } + LOG(DEBUG) << "split total balance from " << old_total_balance << " to our share of " << total_balance_; + // 6. adjust total_fees + LOG(DEBUG) << "split total validator fees (current value is " << total_validator_fees_ << ")"; + total_validator_fees_ = (total_validator_fees_ + is_right_child(subshard)) >> 1; + LOG(DEBUG) << "new total_validator_fees is " << total_validator_fees_; + // NB: if total_fees_extra will be allowed to be non-empty, split it here too + // 7. reset overload/underload history + overload_history_ = underload_history_ = 0; + // 999. anything else? + id_.id.shard = subshard.shard; + id_.file_hash.set_zero(); + id_.root_hash.set_zero(); + return td::Status::OK(); } -bool MsgAddressInt::extract_std_address(vm::CellSlice& cs, ton::WorkchainId& workchain, ton::StdSmcAddress& addr, - bool do_rewrite) const { - if (!cs.have(3 + 8 + 64)) { - return {}; - } - int t = (int)cs.prefetch_ulong(2 + 1 + 5); - switch (t >> 5) { - case 4: { // addr_std$10, anycast=nothing$0 - return cs.advance(3) && cs.fetch_int_to(8, workchain) && cs.fetch_bits_to(addr); - } - case 5: { // addr_std$10, anycast=just$1 (Anycast) - t &= 31; // depth:(## 5) - unsigned long long rewrite; - if (cs.advance(8) && cs.fetch_uint_to(t, rewrite) // rewrite_pfx:(bits depth) - && cs.fetch_int_to(8, workchain) // workchain_id:int8 - && cs.fetch_bits_to(addr)) { // address:bits256 - if (do_rewrite) { - addr.bits().store_uint(rewrite, t); - } - return true; - } - break; +int filter_out_msg_queue(vm::AugmentedDictionary& out_queue, ton::ShardIdFull old_shard, ton::ShardIdFull subshard) { + return out_queue.filter([subshard, old_shard](vm::CellSlice& cs, td::ConstBitPtr key, int key_len) -> int { + CHECK(key_len == 352); + LOG(DEBUG) << "scanning OutMsgQueue entry with key " << key.to_hex(key_len); + block::tlb::MsgEnvelope::Record_std env; + block::gen::CommonMsgInfo::Record_int_msg_info info; + if (!(cs.size_ext() == 0x10080 // (uint64) enqueued_lt:uint64 out_msg:^MsgEnvelope + && tlb::unpack_cell(cs.prefetch_ref(), env) && tlb::unpack_cell_inexact(env.msg, info))) { + LOG(ERROR) << "cannot unpack OutMsgQueue entry with key " << key.to_hex(key_len); + return -1; } - case 6: { // addr_var$11, anycast=nothing$0 - int len; - return cs.advance(3) && cs.fetch_uint_to(9, len) // addr_len:(## 9) - && len == 256 // only 256-bit addresses are standard - && cs.fetch_int_to(32, workchain) // workchain_id:int32 - && cs.fetch_bits_to(addr); // address:(bits addr_len) + auto src_prefix = block::tlb::t_MsgAddressInt.get_prefix(info.src); + auto dest_prefix = block::tlb::t_MsgAddressInt.get_prefix(info.dest); + auto cur_prefix = block::interpolate_addr(src_prefix, dest_prefix, env.cur_addr); + if (!(src_prefix.is_valid() && dest_prefix.is_valid() && cur_prefix.is_valid())) { + LOG(ERROR) << "OutMsgQueue message with key " << key.to_hex(key_len) + << " has invalid source or destination address"; + return -1; } - case 7: { // addr_var$11, anycast=just$1 (Anycast) - t &= 31; // depth:(## 5) - int len; - unsigned long long rewrite; - if (cs.advance(8) && cs.fetch_uint_to(t, rewrite) // rewrite_pfx:(bits depth) - && cs.fetch_uint_to(9, len) // addr_len:(## 9) - && len == 256 // only 256-bit addresses are standard - && cs.fetch_int_to(32, workchain) // workchain_id:int32 - && cs.fetch_bits_to(addr)) { // address:bits256 - if (do_rewrite) { - addr.bits().store_uint(rewrite, t); - } - return true; - } - break; + if (!ton::shard_contains(old_shard, cur_prefix)) { + LOG(ERROR) << "OutMsgQueue message with key " << key.to_hex(key_len) + << " does not contain current address belonging to shard " << old_shard.to_str(); + return -1; } - } - return false; -} - -const MsgAddressInt t_MsgAddressInt; - -bool MsgAddress::validate_skip(vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case addr_none: - case addr_ext: - return t_MsgAddressExt.validate_skip(cs); - case addr_std: - case addr_var: - return t_MsgAddressInt.validate_skip(cs); - } - return false; -} - -const MsgAddress t_MsgAddress; - -bool VarUInteger::skip(vm::CellSlice& cs) const { - int len = (int)cs.fetch_ulong(ln); - return len >= 0 && len < n && cs.advance(len * 8); -} - -bool VarUInteger::validate_skip(vm::CellSlice& cs) const { - int len = (int)cs.fetch_ulong(ln); - return len >= 0 && len < n && (!len || cs.prefetch_ulong(8)) && cs.advance(len * 8); + return ton::shard_contains(subshard, cur_prefix); + }); } -td::RefInt256 VarUInteger::as_integer_skip(vm::CellSlice& cs) const { - int len = (int)cs.fetch_ulong(ln); - return (len >= 0 && len < n && (!len || cs.prefetch_ulong(8))) ? cs.fetch_int256(len * 8, false) : td::RefInt256{}; +bool CurrencyCollection::validate() const { + return is_valid() && td::sgn(grams) >= 0 && validate_extra(); } -unsigned long long VarUInteger::as_uint(const vm::CellSlice& cs) const { - int len = (int)cs.prefetch_ulong(ln); - return len >= 0 && len <= 8 && cs.have(ln + len * 8) ? td::bitstring::bits_load_ulong(cs.data_bits() + ln, len * 8) - : std::numeric_limits::max(); +bool CurrencyCollection::validate_extra() const { + if (extra.is_null()) { + return true; + } + vm::CellBuilder cb; + return cb.store_maybe_ref(extra) && block::tlb::t_ExtraCurrencyCollection.validate_ref(cb.finalize()); } -bool VarUInteger::store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const { - int k = value.bit_size(false); - return k <= (n - 1) * 8 && cb.store_long_bool((k + 7) >> 3, ln) && cb.store_int256_bool(value, (k + 7) & -8, false); +bool CurrencyCollection::add(const CurrencyCollection& a, const CurrencyCollection& b, CurrencyCollection& c) { + return (a.is_valid() && b.is_valid() && (c.grams = a.grams + b.grams).not_null() && c.grams->is_valid() && + add_extra_currency(a.extra, b.extra, c.extra)) || + c.invalidate(); } -unsigned VarUInteger::precompute_integer_size(const td::BigInt256& value) const { - int k = value.bit_size(false); - return k <= (n - 1) * 8 ? ln + ((k + 7) & -8) : 0xfff; +bool CurrencyCollection::add(const CurrencyCollection& a, CurrencyCollection&& b, CurrencyCollection& c) { + return (a.is_valid() && b.is_valid() && (c.grams = a.grams + std::move(b.grams)).not_null() && c.grams->is_valid() && + add_extra_currency(a.extra, std::move(b.extra), c.extra)) || + c.invalidate(); } -unsigned VarUInteger::precompute_integer_size(td::RefInt256 value) const { - if (value.is_null()) { - return 0xfff; +CurrencyCollection& CurrencyCollection::operator+=(const CurrencyCollection& other) { + if (!is_valid()) { + return *this; } - int k = value->bit_size(false); - return k <= (n - 1) * 8 ? ln + ((k + 7) & -8) : 0xfff; -} - -const VarUInteger t_VarUInteger_3{3}, t_VarUInteger_7{7}, t_VarUInteger_16{16}, t_VarUInteger_32{32}; - -bool VarUIntegerPos::skip(vm::CellSlice& cs) const { - int len = (int)cs.fetch_ulong(ln); - return len > 0 && len < n && cs.advance(len * 8); -} - -bool VarUIntegerPos::validate_skip(vm::CellSlice& cs) const { - int len = (int)cs.fetch_ulong(ln); - return len > 0 && len < n && cs.prefetch_ulong(8) && cs.advance(len * 8); + if (!(other.is_valid() && (grams += other.grams).not_null() && grams->is_valid() && + add_extra_currency(extra, other.extra, extra))) { + invalidate(); + } + return *this; } -td::RefInt256 VarUIntegerPos::as_integer_skip(vm::CellSlice& cs) const { - int len = (int)cs.fetch_ulong(ln); - return (len > 0 && len < n && cs.prefetch_ulong(8)) ? cs.fetch_int256(len * 8, false) : td::RefInt256{}; +CurrencyCollection& CurrencyCollection::operator+=(CurrencyCollection&& other) { + if (!is_valid()) { + return *this; + } + if (!(other.is_valid() && (grams += std::move(other.grams)).not_null() && grams->is_valid() && + add_extra_currency(extra, std::move(other.extra), extra))) { + invalidate(); + } + return *this; } -unsigned long long VarUIntegerPos::as_uint(const vm::CellSlice& cs) const { - int len = (int)cs.prefetch_ulong(ln); - return len >= 0 && len <= 8 && cs.have(ln + len * 8) ? td::bitstring::bits_load_ulong(cs.data_bits() + ln, len * 8) - : std::numeric_limits::max(); +CurrencyCollection& CurrencyCollection::operator+=(td::RefInt256 other_grams) { + if (!is_valid()) { + return *this; + } + if (!(other_grams.not_null() && (grams += other_grams).not_null())) { + invalidate(); + } + return *this; } -bool VarUIntegerPos::store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const { - int k = value.bit_size(false); - return k <= (n - 1) * 8 && value.sgn() > 0 && cb.store_long_bool((k + 7) >> 3, ln) && - cb.store_int256_bool(value, (k + 7) & -8, false); +CurrencyCollection CurrencyCollection::operator+(const CurrencyCollection& other) const { + CurrencyCollection res; + add(*this, other, res); + return res; } -const VarUIntegerPos t_VarUIntegerPos_16{16}, t_VarUIntegerPos_32{32}; - -static inline bool redundant_int(vm::CellSlice& cs) { - int t = (int)cs.prefetch_long(9); - return t == 0 || t == -1; +CurrencyCollection CurrencyCollection::operator+(CurrencyCollection&& other) const { + CurrencyCollection res; + add(*this, std::move(other), res); + return res; } -bool VarInteger::skip(vm::CellSlice& cs) const { - int len = (int)cs.fetch_ulong(ln); - return len >= 0 && len < n && cs.advance(len * 8); +CurrencyCollection CurrencyCollection::operator+(td::RefInt256 other_grams) { + if (!is_valid()) { + return *this; + } + auto sum = grams + other_grams; + if (sum.not_null()) { + return CurrencyCollection{std::move(sum), extra}; + } else { + return CurrencyCollection{}; + } } -bool VarInteger::validate_skip(vm::CellSlice& cs) const { - int len = (int)cs.fetch_ulong(ln); - return len >= 0 && len < n && (!len || !redundant_int(cs)) && cs.advance(len * 8); +bool CurrencyCollection::sub(const CurrencyCollection& a, const CurrencyCollection& b, CurrencyCollection& c) { + return (a.is_valid() && b.is_valid() && (c.grams = a.grams - b.grams).not_null() && c.grams->is_valid() && + td::sgn(c.grams) >= 0 && sub_extra_currency(a.extra, b.extra, c.extra)) || + c.invalidate(); } -td::RefInt256 VarInteger::as_integer_skip(vm::CellSlice& cs) const { - int len = (int)cs.fetch_ulong(ln); - return (len >= 0 && len < n && (!len || !redundant_int(cs))) ? cs.fetch_int256(len * 8, true) : td::RefInt256{}; +bool CurrencyCollection::sub(const CurrencyCollection& a, CurrencyCollection&& b, CurrencyCollection& c) { + return (a.is_valid() && b.is_valid() && (c.grams = a.grams - std::move(b.grams)).not_null() && c.grams->is_valid() && + td::sgn(c.grams) >= 0 && sub_extra_currency(a.extra, std::move(b.extra), c.extra)) || + c.invalidate(); } -long long VarInteger::as_int(const vm::CellSlice& cs) const { - int len = (int)cs.prefetch_ulong(ln); - return len >= 0 && len <= 8 && cs.have(ln + len * 8) ? td::bitstring::bits_load_long(cs.data_bits() + ln, len * 8) - : (1ULL << 63); +CurrencyCollection& CurrencyCollection::operator-=(const CurrencyCollection& other) { + if (!is_valid()) { + return *this; + } + if (!(other.is_valid() && (grams -= other.grams).not_null() && grams->is_valid() && td::sgn(grams) >= 0 && + sub_extra_currency(extra, other.extra, extra))) { + invalidate(); + } + return *this; } -bool VarInteger::store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const { - int k = value.bit_size(true); - return k <= (n - 1) * 8 && cb.store_long_bool((k + 7) >> 3, ln) && cb.store_int256_bool(value, (k + 7) & -8, true); +CurrencyCollection& CurrencyCollection::operator-=(CurrencyCollection&& other) { + if (!is_valid()) { + return *this; + } + if (!(other.is_valid() && (grams -= std::move(other.grams)).not_null() && grams->is_valid() && td::sgn(grams) >= 0 && + sub_extra_currency(extra, std::move(other.extra), extra))) { + invalidate(); + } + return *this; } -bool VarIntegerNz::skip(vm::CellSlice& cs) const { - int len = (int)cs.fetch_ulong(ln); - return len > 0 && len < n && cs.advance(len * 8); +CurrencyCollection CurrencyCollection::operator-(const CurrencyCollection& other) const { + CurrencyCollection res; + sub(*this, other, res); + return res; } -bool VarIntegerNz::validate_skip(vm::CellSlice& cs) const { - int len = (int)cs.fetch_ulong(ln); - return len > 0 && len < n && !redundant_int(cs) && cs.advance(len * 8); +CurrencyCollection CurrencyCollection::operator-(CurrencyCollection&& other) const { + CurrencyCollection res; + sub(*this, std::move(other), res); + return res; } -td::RefInt256 VarIntegerNz::as_integer_skip(vm::CellSlice& cs) const { - int len = (int)cs.fetch_ulong(ln); - return (len > 0 && len < n && !redundant_int(cs)) ? cs.fetch_int256(len * 8, true) : td::RefInt256{}; +bool CurrencyCollection::operator==(const CurrencyCollection& other) const { + return is_valid() && other.is_valid() && !td::cmp(grams, other.grams) && + (extra.not_null() == other.extra.not_null()) && + (extra.is_null() || extra->get_hash() == other.extra->get_hash()); } -long long VarIntegerNz::as_int(const vm::CellSlice& cs) const { - int len = (int)cs.prefetch_ulong(ln); - return len >= 0 && len <= 8 && cs.have(ln + len * 8) ? td::bitstring::bits_load_long(cs.data_bits() + ln, len * 8) - : (1ULL << 63); +bool CurrencyCollection::operator>=(const CurrencyCollection& other) const { + Ref tmp; + return is_valid() && other.is_valid() && td::cmp(grams, other.grams) >= 0 && + sub_extra_currency(extra, other.extra, tmp); } -bool VarIntegerNz::store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const { - int k = value.bit_size(true); - return k <= (n - 1) * 8 && value.sgn() != 0 && cb.store_long_bool((k + 7) >> 3, ln) && - cb.store_int256_bool(value, (k + 7) & -8, true); +bool CurrencyCollection::store(vm::CellBuilder& cb) const { + return is_valid() && store_CurrencyCollection(cb, grams, extra); } -bool Grams::validate_skip(vm::CellSlice& cs) const { - return t_VarUInteger_16.validate_skip(cs); +bool CurrencyCollection::fetch(vm::CellSlice& cs) { + return fetch_CurrencyCollection(cs, grams, extra) || invalidate(); } -td::RefInt256 Grams::as_integer_skip(vm::CellSlice& cs) const { - return t_VarUInteger_16.as_integer_skip(cs); +bool CurrencyCollection::unpack(Ref csr) { + return unpack_CurrencyCollection(std::move(csr), grams, extra) || invalidate(); } -bool Grams::null_value(vm::CellBuilder& cb) const { - return t_VarUInteger_16.null_value(cb); +bool CurrencyCollection::validate_unpack(Ref csr) { + return (csr.not_null() && block::tlb::t_CurrencyCollection.validate(*csr) && + unpack_CurrencyCollection(std::move(csr), grams, extra)) || + invalidate(); } -bool Grams::store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const { - return t_VarUInteger_16.store_integer_value(cb, value); +bool CurrencyCollection::show(std::ostream& os) const { + if (!is_valid()) { + os << ""; + return false; + } + if (extra.not_null()) { + os << '('; + } + os << grams << "ng"; + if (extra.not_null()) { + vm::Dictionary dict{extra, 32}; + if (!dict.check_for_each([&os](Ref csr, td::ConstBitPtr key, int n) { + CHECK(n == 32); + int x = (int)key.get_int(n); + auto val = block::tlb::t_VarUIntegerPos_32.as_integer_skip(csr.write()); + if (val.is_null() || !csr->empty_ext()) { + os << "+.$" << x << "...)"; + return false; + } + os << '+' << val << ".$" << x; + return true; + })) { + return false; + } + os << ')'; + } + return true; } -unsigned Grams::precompute_size(const td::BigInt256& value) const { - return t_VarUInteger_16.precompute_integer_size(value); +std::string CurrencyCollection::to_str() const { + std::ostringstream os; + show(os); + return os.str(); } -unsigned Grams::precompute_size(td::RefInt256 value) const { - return t_VarUInteger_16.precompute_integer_size(std::move(value)); +std::ostream& operator<<(std::ostream& os, const CurrencyCollection& cc) { + cc.show(os); + return os; } -const Grams t_Grams; - -const Unary t_Unary; - -bool HmLabel::validate_skip(vm::CellSlice& cs, int& n) const { - switch (get_tag(cs)) { - case hml_short: - return cs.advance(1) && (n = cs.count_leading(1)) <= m && cs.advance(2 * n + 1); - case hml_long: - return cs.advance(2) && cs.fetch_uint_leq(m, n) && cs.advance(n); - case hml_same: - return cs.advance(3) && cs.fetch_uint_leq(m, n); +bool ValueFlow::validate() const { + return is_valid() && + from_prev_blk + imported + fees_imported + created + minted == to_next_blk + exported + fees_collected; +} + +bool ValueFlow::store(vm::CellBuilder& cb) const { + vm::CellBuilder cb2; + return cb.store_long_bool(block::gen::ValueFlow::cons_tag[0], 32) // value_flow ^[ + && from_prev_blk.store(cb2) // from_prev_blk:CurrencyCollection + && to_next_blk.store(cb2) // to_next_blk:CurrencyCollection + && imported.store(cb2) // imported:CurrencyCollection + && exported.store(cb2) // exported:CurrencyCollection + && cb.store_ref_bool(cb2.finalize()) // ] + && fees_collected.store(cb) // fees_collected:CurrencyCollection + && fees_imported.store(cb2) // ^[ fees_imported:CurrencyCollection + && created.store(cb2) // created:CurrencyCollection + && minted.store(cb2) // minted:CurrencyCollection + && cb.store_ref_bool(cb2.finalize()); // ] = ValueFlow; +} + +bool ValueFlow::fetch(vm::CellSlice& cs) { + block::gen::ValueFlow::Record f; + if (!(tlb::unpack(cs, f) && from_prev_blk.validate_unpack(std::move(f.r1.from_prev_blk)) && + to_next_blk.validate_unpack(std::move(f.r1.to_next_blk)) && + imported.validate_unpack(std::move(f.r1.imported)) && exported.validate_unpack(std::move(f.r1.exported)) && + fees_collected.validate_unpack(std::move(f.fees_collected)) && + fees_imported.validate_unpack(std::move(f.r2.fees_imported)) && + created.validate_unpack(std::move(f.r2.created)) && minted.validate_unpack(std::move(f.r2.minted)))) { + return invalidate(); } - return false; + return true; } -int HmLabel::get_tag(const vm::CellSlice& cs) const { - int tag = (int)cs.prefetch_ulong(2); - return tag != 1 ? tag : hml_short; +bool ValueFlow::unpack(Ref csr) { + return (csr.not_null() && fetch(csr.write()) && csr->empty_ext()) || invalidate(); } -int HashmapNode::get_size(const vm::CellSlice& cs) const { - assert(n >= 0); - return n ? 0x20000 : value_type.get_size(cs); +static inline bool say(std::ostream& os, const char* str) { + os << str; + return true; } -bool HashmapNode::skip(vm::CellSlice& cs) const { - assert(n >= 0); - return n ? cs.advance_refs(2) : value_type.skip(cs); +bool ValueFlow::show_one(std::ostream& os, const char* str, const CurrencyCollection& cc) const { + return say(os, str) && cc.show(os); } -bool HashmapNode::validate_skip(vm::CellSlice& cs) const { - assert(n >= 0); - if (!n) { - // hmn_leaf - return value_type.validate_skip(cs); - } else { - // hmn_fork - Hashmap branch_type{n - 1, value_type}; - return branch_type.validate_ref(cs.fetch_ref()) && branch_type.validate_ref(cs.fetch_ref()); +bool ValueFlow::show(std::ostream& os) const { + if (!is_valid()) { + os << ""; + return false; } + return (say(os, "(value-flow ") && show_one(os, "from_prev_blk:", from_prev_blk) && + show_one(os, " to_next_blk:", to_next_blk) && show_one(os, " imported:", imported) && + show_one(os, " exported:", exported) && show_one(os, " fees_collected:", fees_collected) && + show_one(os, " fees_imported:", fees_imported) && show_one(os, " created:", created) && + show_one(os, " minted:", minted) && say(os, ")")) || + (say(os, "...)") && false); } -bool Hashmap::skip(vm::CellSlice& cs) const { - int l; - return HmLabel{n}.skip(cs, l) && HashmapNode{n - l, value_type}.skip(cs); +std::string ValueFlow::to_str() const { + std::ostringstream os; + show(os); + return os.str(); } -bool Hashmap::validate_skip(vm::CellSlice& cs) const { - int l; - return HmLabel{n}.skip(cs, l) && HashmapNode{n - l, value_type}.skip(cs); +std::ostream& operator<<(std::ostream& os, const ValueFlow& vflow) { + vflow.show(os); + return os; } -int HashmapE::get_size(const vm::CellSlice& cs) const { - int tag = get_tag(cs); - return (tag >= 0 ? (tag > 0 ? 0x10001 : 1) : -1); -} +/* + * + * Other block-related functions + * + */ -bool HashmapE::validate(const vm::CellSlice& cs) const { - int tag = get_tag(cs); - return tag <= 0 ? !tag : root_type.validate_ref(cs.prefetch_ref()); +bool store_UInt7(vm::CellBuilder& cb, unsigned long long value) { + return block::tlb::t_VarUInteger_7.store_long(cb, (long long)value); } -bool HashmapE::add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const { - int n = root_type.n; - vm::Dictionary dict1{vm::DictAdvance(), cs1, n}, dict2{vm::DictAdvance(), cs2, n}; - const TLB& vt = root_type.value_type; - vm::Dictionary::simple_combine_func_t combine = [vt](vm::CellBuilder& cb, Ref cs1_ref, - Ref cs2_ref) -> bool { - if (!vt.add_values(cb, cs1_ref.write(), cs2_ref.write())) { - throw CombineError{}; - } - return true; - }; - return dict1.combine_with(dict2, combine) && std::move(dict1).append_dict_to_bool(cb); -} - -bool HashmapE::add_values_ref(Ref& res, Ref arg1, Ref arg2) const { - int n = root_type.n; - vm::Dictionary dict1{std::move(arg1), n}, dict2{std::move(arg2), n}; - const TLB& vt = root_type.value_type; - vm::Dictionary::simple_combine_func_t combine = [vt](vm::CellBuilder& cb, Ref cs1_ref, - Ref cs2_ref) -> bool { - if (!vt.add_values(cb, cs1_ref.write(), cs2_ref.write())) { - throw CombineError{}; - } - return true; - }; - if (dict1.combine_with(dict2, combine)) { - dict2.reset(); - res = std::move(dict1).extract_root_cell(); - return true; - } else { - res = Ref{}; - return false; - } +bool store_UInt7(vm::CellBuilder& cb, unsigned long long value1, unsigned long long value2) { + return store_UInt7(cb, value1) && store_UInt7(cb, value2); } -int HashmapE::sub_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const { - int n = root_type.n; - vm::Dictionary dict1{vm::DictAdvance(), cs1, n}, dict2{vm::DictAdvance(), cs2, n}; - const TLB& vt = root_type.value_type; - vm::Dictionary::simple_combine_func_t combine = [vt](vm::CellBuilder& cb, Ref cs1_ref, - Ref cs2_ref) -> bool { - int r = vt.sub_values(cb, cs1_ref.write(), cs2_ref.write()); - if (r < 0) { - throw CombineError{}; - } - return r; - }; - if (!dict1.combine_with(dict2, combine, 1)) { - return -1; - } - dict2.reset(); - bool not_empty = !dict1.is_empty(); - return std::move(dict1).append_dict_to_bool(cb) ? not_empty : -1; -} - -int HashmapE::sub_values_ref(Ref& res, Ref arg1, Ref arg2) const { - int n = root_type.n; - vm::Dictionary dict1{std::move(arg1), n}, dict2{std::move(arg2), n}; - const TLB& vt = root_type.value_type; - vm::Dictionary::simple_combine_func_t combine = [vt](vm::CellBuilder& cb, Ref cs1_ref, - Ref cs2_ref) -> bool { - int r = vt.sub_values(cb, cs1_ref.write(), cs2_ref.write()); - if (r < 0) { - throw CombineError{}; - } - return r; - }; - if (dict1.combine_with(dict2, combine, 1)) { - dict2.reset(); - res = std::move(dict1).extract_root_cell(); - return res.not_null(); +bool store_Maybe_Grams(vm::CellBuilder& cb, td::RefInt256 value) { + if (value.is_null()) { + return cb.store_long_bool(0, 1); } else { - res = Ref{}; - return -1; + return cb.store_long_bool(1, 1) && block::tlb::t_Grams.store_integer_ref(cb, std::move(value)); } } -bool HashmapE::store_ref(vm::CellBuilder& cb, Ref arg) const { - if (arg.is_null()) { +bool store_Maybe_Grams_nz(vm::CellBuilder& cb, td::RefInt256 value) { + if (value.is_null() || !value->sgn()) { return cb.store_long_bool(0, 1); } else { - return cb.store_long_bool(1, 1) && cb.store_ref_bool(std::move(arg)); + return cb.store_long_bool(1, 1) && block::tlb::t_Grams.store_integer_ref(cb, std::move(value)); } } -const ExtraCurrencyCollection t_ExtraCurrencyCollection; - -bool CurrencyCollection::validate_skip(vm::CellSlice& cs) const { - return t_Grams.validate_skip(cs) && t_ExtraCurrencyCollection.validate_skip(cs); +bool store_CurrencyCollection(vm::CellBuilder& cb, td::RefInt256 value, Ref extra) { + return block::tlb::t_CurrencyCollection.pack_special(cb, std::move(value), std::move(extra)); } -bool CurrencyCollection::skip(vm::CellSlice& cs) const { - return t_Grams.skip(cs) && t_ExtraCurrencyCollection.skip(cs); +bool fetch_CurrencyCollection(vm::CellSlice& cs, td::RefInt256& value, Ref& extra) { + return block::tlb::t_CurrencyCollection.unpack_special(cs, value, extra); } -td::RefInt256 CurrencyCollection::as_integer_skip(vm::CellSlice& cs) const { - auto res = t_Grams.as_integer_skip(cs); - if (res.not_null() && t_ExtraCurrencyCollection.skip(cs)) { - return res; +bool unpack_CurrencyCollection(Ref csr, td::RefInt256& value, Ref& extra) { + if (csr.is_null()) { + return false; + } else if (csr->is_unique()) { + return block::tlb::t_CurrencyCollection.unpack_special(csr.write(), value, extra); } else { - return {}; + vm::CellSlice cs{*csr}; + return block::tlb::t_CurrencyCollection.unpack_special(cs, value, extra); } } -bool CurrencyCollection::add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const { - return t_Grams.add_values(cb, cs1, cs2) && t_ExtraCurrencyCollection.add_values(cb, cs1, cs2); -} - -bool CurrencyCollection::unpack_special(vm::CellSlice& cs, td::RefInt256& balance, Ref& extra) const { - balance = t_Grams.as_integer_skip(cs); - if (cs.fetch_ulong(1) == 1) { - return balance.not_null() && cs.fetch_ref_to(extra) && cs.empty_ext(); - } else { - extra.clear(); - return balance.not_null() && cs.empty_ext(); +bool check_one_library(Ref cs_ref, td::ConstBitPtr key, int n) { + assert(n == 256); + if (cs_ref->size_ext() != 0x10001) { + return false; } + Ref cell = cs_ref->prefetch_ref(); + const auto& cell_hash = cell->get_hash(); + return !td::bitstring::bits_memcmp(cell_hash.bits(), key, n); } -bool CurrencyCollection::pack_special(vm::CellBuilder& cb, td::RefInt256 balance, Ref extra) const { - return t_Grams.store_integer_ref(cb, std::move(balance)) && t_ExtraCurrencyCollection.store_ref(cb, std::move(extra)); -} - -const CurrencyCollection t_CurrencyCollection; - -bool CommonMsgInfo::validate_skip(vm::CellSlice& cs) const { - int tag = get_tag(cs); - switch (tag) { - case int_msg_info: - return cs.advance(4) // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool - && t_MsgAddressInt.validate_skip(cs) // src - && t_MsgAddressInt.validate_skip(cs) // dest - && t_CurrencyCollection.validate_skip(cs) // value - && t_Grams.validate_skip(cs) // ihr_fee - && t_Grams.validate_skip(cs) // fwd_fee - && cs.advance(64 + 32); // created_lt:uint64 created_at:uint32 - case ext_in_msg_info: - return cs.advance(2) && t_MsgAddressExt.validate_skip(cs) // src - && t_MsgAddressInt.validate_skip(cs) // dest - && t_Grams.validate_skip(cs); // import_fee - case ext_out_msg_info: - return cs.advance(2) && t_MsgAddressInt.validate_skip(cs) // src - && t_MsgAddressExt.validate_skip(cs) // dest - && cs.advance(64 + 32); // created_lt:uint64 created_at:uint32 +bool valid_library_collection(Ref cell, bool catch_errors) { + if (cell.is_null()) { + return true; } - return false; -} - -bool CommonMsgInfo::unpack(vm::CellSlice& cs, CommonMsgInfo::Record_int_msg_info& data) const { - return get_tag(cs) == int_msg_info && cs.advance(1) && cs.fetch_bool_to(data.ihr_disabled) && - cs.fetch_bool_to(data.bounce) && cs.fetch_bool_to(data.bounced) && t_MsgAddressInt.fetch_to(cs, data.src) && - t_MsgAddressInt.fetch_to(cs, data.dest) && t_CurrencyCollection.fetch_to(cs, data.value) && - t_Grams.fetch_to(cs, data.ihr_fee) && t_Grams.fetch_to(cs, data.fwd_fee) && - cs.fetch_uint_to(64, data.created_lt) && cs.fetch_uint_to(32, data.created_at); -} - -bool CommonMsgInfo::skip(vm::CellSlice& cs) const { - int tag = get_tag(cs); - switch (tag) { - case int_msg_info: - return cs.advance(4) // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool - && t_MsgAddressInt.skip(cs) // src - && t_MsgAddressInt.skip(cs) // dest - && t_CurrencyCollection.skip(cs) // value - && t_Grams.skip(cs) // ihr_fee - && t_Grams.skip(cs) // fwd_fee - && cs.advance(64 + 32); // created_lt:uint64 created_at:uint32 - case ext_in_msg_info: - return cs.advance(2) && t_MsgAddressExt.skip(cs) // src - && t_MsgAddressInt.skip(cs) // dest - && t_Grams.skip(cs); // import_fee - case ext_out_msg_info: - return cs.advance(2) && t_MsgAddressInt.skip(cs) // src - && t_MsgAddressExt.skip(cs) // dest - && cs.advance(64 + 32); // created_lt:uint64 created_at:uint32 + if (!catch_errors) { + vm::Dictionary dict{std::move(cell), 256}; + return dict.check_for_each(check_one_library); } - return false; -} - -bool CommonMsgInfo::get_created_lt(vm::CellSlice& cs, unsigned long long& created_lt) const { - switch (get_tag(cs)) { - case int_msg_info: - return cs.advance(4) // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool - && t_MsgAddressInt.skip(cs) // src - && t_MsgAddressInt.skip(cs) // dest - && t_CurrencyCollection.skip(cs) // value - && t_Grams.skip(cs) // ihr_fee - && t_Grams.skip(cs) // fwd_fee - && cs.fetch_ulong_bool(64, created_lt) // created_lt:uint64 - && cs.advance(32); // created_at:uint32 - case ext_in_msg_info: - return false; - case ext_out_msg_info: - return cs.advance(2) && t_MsgAddressInt.skip(cs) // src - && t_MsgAddressExt.skip(cs) // dest - && cs.fetch_ulong_bool(64, created_lt) // created_lt:uint64 - && cs.advance(32); // created_at:uint32 + try { + vm::Dictionary dict{std::move(cell), 256}; + return dict.check_for_each(check_one_library); + } catch (vm::VmError&) { + return false; } - return false; -} - -const CommonMsgInfo t_CommonMsgInfo; -const TickTock t_TickTock; -const RefAnything t_RefCell; - -bool StateInit::validate_skip(vm::CellSlice& cs) const { - return Maybe{5}.validate_skip(cs) // split_depth:(Maybe (## 5)) - && Maybe{}.validate_skip(cs) // special:(Maybe TickTock) - && Maybe{}.validate_skip(cs) // code:(Maybe ^Cell) - && Maybe{}.validate_skip(cs) // data:(Maybe ^Cell) - && Maybe{}.validate_skip(cs); // library:(Maybe ^Cell) } -bool StateInit::get_ticktock(vm::CellSlice& cs, int& ticktock) const { - bool have_tt; - ticktock = 0; - return Maybe{5}.validate_skip(cs) && cs.fetch_bool_to(have_tt) && (!have_tt || cs.fetch_uint_to(2, ticktock)); -} - -const StateInit t_StateInit; - -bool Message::validate_skip(vm::CellSlice& cs) const { - static const Maybe>> init_type; - static const Either body_type; - return t_CommonMsgInfo.validate_skip(cs) // info:CommonMsgInfo - && init_type.validate_skip(cs) // init:(Maybe (Either StateInit ^StateInit)) - && body_type.validate_skip(cs); // body:(Either X ^X) -} - -bool Message::extract_info(vm::CellSlice& cs) const { - return t_CommonMsgInfo.extract(cs); -} - -bool Message::get_created_lt(vm::CellSlice& cs, unsigned long long& created_lt) const { - return t_CommonMsgInfo.get_created_lt(cs, created_lt); -} - -bool Message::is_internal(Ref ref) const { - return is_internal(load_cell_slice(std::move(ref))); -} - -const Message t_Message; -const RefTo t_Ref_Message; - -bool IntermediateAddress::validate_skip(vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case interm_addr_regular: - return cs.advance(1) && cs.fetch_ulong(7) <= 96U; - case interm_addr_simple: - return cs.advance(2 + 8 + 64); - case interm_addr_ext: - if (cs.have(2 + 32 + 64)) { - cs.advance(2); - int workchain_id = (int)cs.fetch_long(32); - return (workchain_id < -128 || workchain_id >= 128) && cs.advance(64); - } - // no break +bool check_one_config_param(Ref cs_ref, td::ConstBitPtr key, td::ConstBitPtr addr, bool relax_par0) { + if (cs_ref->size_ext() != 0x10000) { + return false; } - return false; + Ref cell = cs_ref->prefetch_ref(); + int idx = (int)key.get_int(32); + if (!idx) { + auto cs = load_cell_slice(std::move(cell)); + return cs.size_ext() == 256 && (relax_par0 || cs.fetch_bits(256) == addr); + } else if (idx < 0) { + return true; + } + bool ok = block::gen::ConfigParam{idx}.validate_ref(std::move(cell)); + if (!ok) { + LOG(ERROR) << "configuration parameter #" << idx << " is invalid"; + } + return ok; } -bool IntermediateAddress::skip(vm::CellSlice& cs) const { - return cs.advance(get_size(cs)); -} - -int IntermediateAddress::get_size(const vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case interm_addr_regular: - return 1 + 7; - case interm_addr_simple: - return 2 + 8 + 64; - case interm_addr_ext: - return 2 + 32 + 64; - } - return -1; -} - -const IntermediateAddress t_IntermediateAddress; - -bool MsgEnvelope::validate_skip(vm::CellSlice& cs) const { - return cs.fetch_ulong(4) == 4 // msg_envelope#4 - && t_IntermediateAddress.validate_skip(cs) // cur_addr:IntermediateAddress - && t_IntermediateAddress.validate_skip(cs) // next_addr:IntermediateAddress - && t_Grams.validate_skip(cs) // fwd_fee_remaining:Grams - && t_Ref_Message.validate_skip(cs); // msg:^Message -} - -bool MsgEnvelope::skip(vm::CellSlice& cs) const { - return cs.advance(4) // msg_envelope#4 - && t_IntermediateAddress.skip(cs) // cur_addr:IntermediateAddress - && t_IntermediateAddress.skip(cs) // next_addr:IntermediateAddress - && t_Grams.skip(cs) // fwd_fee_remaining:Grams - && t_Ref_Message.skip(cs); // msg:^Message -} - -bool MsgEnvelope::extract_fwd_fees_remaining(vm::CellSlice& cs) const { - return t_IntermediateAddress.skip(cs) && t_IntermediateAddress.skip(cs) && t_Grams.extract(cs); -} - -bool MsgEnvelope::unpack(vm::CellSlice& cs, MsgEnvelope::Record& data) const { - return cs.fetch_ulong(4) == 4 // msg_envelope#4 - && t_IntermediateAddress.fetch_to(cs, data.cur_addr) // cur_addr:IntermediateAddress - && t_IntermediateAddress.fetch_to(cs, data.next_addr) // next_addr:IntermediateAddress - && t_Grams.fetch_to(cs, data.fwd_fee_remaining) // fwd_fee_remaining:Grams - && cs.fetch_ref_to(data.msg); // msg:^Message -} - -bool MsgEnvelope::unpack(vm::CellSlice& cs, MsgEnvelope::Record_std& data) const { - return cs.fetch_ulong(4) == 4 // msg_envelope#4 - && t_IntermediateAddress.fetch_regular(cs, data.cur_addr) // cur_addr:IntermediateAddress - && t_IntermediateAddress.fetch_regular(cs, data.next_addr) // next_addr:IntermediateAddress - && t_Grams.as_integer_skip_to(cs, data.fwd_fee_remaining) // fwd_fee_remaining:Grams - && cs.fetch_ref_to(data.msg); // msg:^Message -} - -bool MsgEnvelope::unpack_std(vm::CellSlice& cs, int& cur_a, int& nhop_a, Ref& msg) const { - return cs.fetch_ulong(4) == 4 // msg_envelope#4 - && t_IntermediateAddress.fetch_regular(cs, cur_a) // cur_addr:IntermediateAddress - && t_IntermediateAddress.fetch_regular(cs, nhop_a) // next_addr:IntermediateAddress - && cs.fetch_ref_to(msg); -} - -bool MsgEnvelope::get_created_lt(const vm::CellSlice& cs, unsigned long long& created_lt) const { - if (!cs.size_refs()) { - return false; - } - auto msg_cs = load_cell_slice(cs.prefetch_ref()); - return t_Message.get_created_lt(msg_cs, created_lt); -} - -const MsgEnvelope t_MsgEnvelope; -const RefTo t_Ref_MsgEnvelope; - -bool StorageUsed::validate_skip(vm::CellSlice& cs) const { - return t_VarUInteger_7.validate_skip(cs) // cells:(VarUInteger 7) - && t_VarUInteger_7.validate_skip(cs) // bits:(VarUInteger 7) - && t_VarUInteger_7.validate_skip(cs); // public_cells:(VarUInteger 7) -} - -bool StorageUsed::skip(vm::CellSlice& cs) const { - return t_VarUInteger_7.skip(cs) // cells:(VarUInteger 7) - && t_VarUInteger_7.skip(cs) // bits:(VarUInteger 7) - && t_VarUInteger_7.skip(cs); // public_cells:(VarUInteger 7) -} - -const StorageUsed t_StorageUsed; - -bool StorageUsedShort::validate_skip(vm::CellSlice& cs) const { - return t_VarUInteger_7.validate_skip(cs) // cells:(VarUInteger 7) - && t_VarUInteger_7.validate_skip(cs); // bits:(VarUInteger 7) -} - -bool StorageUsedShort::skip(vm::CellSlice& cs) const { - return t_VarUInteger_7.skip(cs) // cells:(VarUInteger 7) - && t_VarUInteger_7.skip(cs); // bits:(VarUInteger 7) -} - -const StorageUsedShort t_StorageUsedShort; - -const Maybe t_Maybe_Grams; - -bool StorageInfo::skip(vm::CellSlice& cs) const { - return t_StorageUsed.skip(cs) // used:StorageUsed - && cs.advance(32) // last_paid:uint32 - && t_Maybe_Grams.skip(cs); // due_payment:(Maybe Grams) -} - -bool StorageInfo::validate_skip(vm::CellSlice& cs) const { - return t_StorageUsed.validate_skip(cs) // used:StorageUsed - && cs.advance(32) // last_paid:uint32 - && t_Maybe_Grams.validate_skip(cs); // due_payment:(Maybe Grams) -} - -const StorageInfo t_StorageInfo; - -bool AccountState::validate_skip(vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case account_uninit: - return cs.advance(2); - case account_frozen: - return cs.advance(2 + 256); - case account_active: - return cs.advance(1) && t_StateInit.validate_skip(cs); - } - return false; -} - -bool AccountState::get_ticktock(vm::CellSlice& cs, int& ticktock) const { - if (get_tag(cs) != account_active) { - ticktock = 0; - return true; - } - return cs.advance(1) && t_StateInit.get_ticktock(cs, ticktock); -} - -const AccountState t_AccountState; - -bool AccountStorage::skip(vm::CellSlice& cs) const { - return cs.advance(64) && t_CurrencyCollection.skip(cs) && t_AccountState.skip(cs); -} - -bool AccountStorage::skip_copy_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const { - return cs.advance(64) && t_CurrencyCollection.skip_copy(cb, cs) && t_AccountState.skip(cs); -} - -bool AccountStorage::validate_skip(vm::CellSlice& cs) const { - return cs.advance(64) && t_CurrencyCollection.validate_skip(cs) && t_AccountState.validate_skip(cs); -} - -const AccountStorage t_AccountStorage; - -bool Account::skip(vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case account_none: - return cs.advance(1); - case account: - return cs.advance(1) // account$1 - && t_MsgAddressInt.skip(cs) // addr:MsgAddressInt - && t_StorageInfo.skip(cs) // storage_stat:StorageInfo - && t_AccountStorage.skip(cs); // storage:AccountStorage - } - return false; -} - -bool Account::validate_skip(vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case account_none: - return allow_empty && cs.advance(1); - case account: - return cs.advance(1) // account$1 - && t_MsgAddressInt.validate_skip(cs) // addr:MsgAddressInt - && t_StorageInfo.validate_skip(cs) // storage_stat:StorageInfo - && t_AccountStorage.validate_skip(cs); // storage:AccountStorage - } - return false; -} - -bool Account::skip_copy_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case account_none: - return allow_empty && cs.advance(1) && t_CurrencyCollection.null_value(cb); - case account: - return cs.advance(1) // account$1 - && t_MsgAddressInt.skip(cs) // addr:MsgAddressInt - && t_StorageInfo.skip(cs) // storage_stat:StorageInfo - && t_AccountStorage.skip_copy_balance(cb, cs); // storage:AccountStorage - } - return false; -} - -bool Account::skip_copy_depth_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const { - int depth; - switch (get_tag(cs)) { - case account_none: - return allow_empty && cs.advance(1) && t_DepthBalanceInfo.null_value(cb); - case account: - return cs.advance(1) // account$1 - && t_MsgAddressInt.skip_get_depth(cs, depth) // addr:MsgAddressInt - && cb.store_uint_leq(30, depth) // -> store split_depth:(#<= 30) - && t_StorageInfo.skip(cs) // storage_stat:StorageInfo - && t_AccountStorage.skip_copy_balance(cb, cs); // storage:AccountStorage - } - return false; -} - -const Account t_Account, t_AccountE{true}; -const RefTo t_Ref_Account; -const ShardAccount t_ShardAccount; - -const AccountStatus t_AccountStatus; - -bool HashmapAugNode::skip(vm::CellSlice& cs) const { - if (n < 0) { - return false; - } else if (!n) { - // ahmn_leaf - return aug.extra_type.skip(cs) && aug.value_type.skip(cs); - } else { - // ahmn_fork - return cs.advance_refs(2) && aug.extra_type.skip(cs); - } -} - -bool HashmapAugNode::validate_skip(vm::CellSlice& cs) const { - if (n < 0) { - return false; - } - if (!n) { - // ahmn_leaf - vm::CellSlice cs_extra{cs}; - if (!aug.extra_type.validate_skip(cs)) { - return false; - } - cs_extra.cut_tail(cs); - vm::CellSlice cs_value{cs}; - if (!aug.value_type.validate_skip(cs)) { - return false; - } - cs_value.cut_tail(cs); - return aug.check_leaf(cs_extra, cs_value); - } - // ahmn_fork - if (!cs.have_refs(2)) { - return false; - } - HashmapAug branch_type{n - 1, aug}; - if (!branch_type.validate_ref(cs.prefetch_ref(0)) || !branch_type.validate_ref(cs.prefetch_ref(1))) { - return false; - } - auto cs_left = load_cell_slice(cs.fetch_ref()); - auto cs_right = load_cell_slice(cs.fetch_ref()); - vm::CellSlice cs_extra{cs}; - if (!aug.extra_type.validate_skip(cs)) { - return false; - } - cs_extra.cut_tail(cs); - return branch_type.extract_extra(cs_left) && branch_type.extract_extra(cs_right) && - aug.check_fork(cs_extra, cs_left, cs_right); -} - -bool HashmapAug::skip(vm::CellSlice& cs) const { - int l; - return HmLabel{n}.skip(cs, l) && HashmapAugNode{n - l, aug}.skip(cs); -} - -bool HashmapAug::validate_skip(vm::CellSlice& cs) const { - int l; - return HmLabel{n}.validate_skip(cs, l) && HashmapAugNode{n - l, aug}.validate_skip(cs); -} - -bool HashmapAug::extract_extra(vm::CellSlice& cs) const { - int l; - return HmLabel{n}.skip(cs, l) && (l == n || cs.advance_refs(2)) && aug.extra_type.extract(cs); -} - -bool HashmapAugE::validate_skip(vm::CellSlice& cs) const { - Ref extra; - switch (get_tag(cs)) { - case ahme_empty: - return cs.advance(1) && (extra = root_type.aug.extra_type.validate_fetch(cs)).not_null() && - root_type.aug.check_empty(extra.unique_write()); - case ahme_root: - if (cs.advance(1) && root_type.validate_ref(cs.prefetch_ref())) { - auto cs_root = load_cell_slice(cs.fetch_ref()); - return (extra = root_type.aug.extra_type.validate_fetch(cs)).not_null() && root_type.extract_extra(cs_root) && - extra->contents_equal(cs_root); - } - break; - } - return false; -} - -bool HashmapAugE::skip(vm::CellSlice& cs) const { - int tag = (int)cs.fetch_ulong(1); - return tag >= 0 && cs.advance_refs(tag) && root_type.aug.extra_type.skip(cs); -} - -bool HashmapAugE::extract_extra(vm::CellSlice& cs) const { - int tag = (int)cs.fetch_ulong(1); - return tag >= 0 && cs.advance_refs(tag) && root_type.aug.extra_type.extract(cs); -} - -bool DepthBalanceInfo::skip(vm::CellSlice& cs) const { - return cs.advance(5) && - t_CurrencyCollection.skip( - cs); // depth_balance$_ split_depth:(#<= 30) balance:CurrencyCollection = DepthBalanceInfo; -} - -bool DepthBalanceInfo::validate_skip(vm::CellSlice& cs) const { - return cs.fetch_ulong(5) <= 30 && - t_CurrencyCollection.validate_skip(cs); // depth_balance$_ split_depth:(#<= 30) balance:CurrencyCollection -} - -bool DepthBalanceInfo::null_value(vm::CellBuilder& cb) const { - return cb.store_zeroes_bool(5) && t_CurrencyCollection.null_value(cb); -} - -bool DepthBalanceInfo::add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const { - unsigned d1, d2; - return cs1.fetch_uint_leq(30, d1) && cs2.fetch_uint_leq(30, d2) && cb.store_uint_leq(30, std::max(d1, d2)) && - t_CurrencyCollection.add_values(cb, cs1, cs2); -} - -const DepthBalanceInfo t_DepthBalanceInfo; - -bool Aug_ShardAccounts::eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const { - if (cs.have_refs()) { - auto cs2 = load_cell_slice(cs.prefetch_ref()); - return t_Account.skip_copy_depth_balance(cb, cs2); - } else { - return false; - } -} - -const Aug_ShardAccounts aug_ShardAccounts; - -const ShardAccounts t_ShardAccounts; - -const AccStatusChange t_AccStatusChange; - -bool TrStoragePhase::skip(vm::CellSlice& cs) const { - return t_Grams.skip(cs) // storage_fees_collected:Grams - && t_Maybe_Grams.skip(cs) // storage_fees_due:Grams - && t_AccStatusChange.skip(cs); // status_change:AccStatusChange -} - -bool TrStoragePhase::validate_skip(vm::CellSlice& cs) const { - return t_Grams.validate_skip(cs) // storage_fees_collected:Grams - && t_Maybe_Grams.validate_skip(cs) // storage_fees_due:Grams - && t_AccStatusChange.validate_skip(cs); // status_change:AccStatusChange -} - -const TrStoragePhase t_TrStoragePhase; - -bool TrCreditPhase::skip(vm::CellSlice& cs) const { - return t_Maybe_Grams.skip(cs) // due_fees_collected:(Maybe Grams) - && t_CurrencyCollection.skip(cs); // credit:CurrencyCollection -} - -bool TrCreditPhase::validate_skip(vm::CellSlice& cs) const { - return t_Maybe_Grams.validate_skip(cs) // due_fees_collected:(Maybe Grams) - && t_CurrencyCollection.validate_skip(cs); // credit:CurrencyCollection -} - -const TrCreditPhase t_TrCreditPhase; - -bool TrComputeInternal1::skip(vm::CellSlice& cs) const { - return t_VarUInteger_7.skip(cs) // gas_used:(VarUInteger 7) - && t_VarUInteger_7.skip(cs) // gas_limit:(VarUInteger 7) - && Maybe{3}.skip(cs) // gas_credit:(Maybe (VarUInteger 3)) - && cs.advance(8 + 32) // mode:int8 exit_code:int32 - && Maybe{32}.skip(cs) // exit_arg:(Maybe int32) - && cs.advance(32 + 256 + 256); // vm_steps:uint32 - // vm_init_state_hash:uint256 - // vm_final_state_hash:uint256 -} - -bool TrComputeInternal1::validate_skip(vm::CellSlice& cs) const { - return t_VarUInteger_7.validate_skip(cs) // gas_used:(VarUInteger 7) - && t_VarUInteger_7.validate_skip(cs) // gas_limit:(VarUInteger 7) - && Maybe{3}.validate_skip(cs) // gas_credit:(Maybe (VarUInteger 3)) - && cs.advance(8 + 32) // mode:int8 exit_code:int32 - && Maybe{32}.validate_skip(cs) // exit_arg:(Maybe int32) - && cs.advance(32 + 256 + 256); // vm_steps:uint32 - // vm_init_state_hash:uint256 - // vm_final_state_hash:uint256 -} - -const TrComputeInternal1 t_TrComputeInternal1; -const RefTo t_Ref_TrComputeInternal1; -const ComputeSkipReason t_ComputeSkipReason; - -bool TrComputePhase::skip(vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case tr_phase_compute_skipped: - return cs.advance(1) && t_ComputeSkipReason.skip(cs); - case tr_phase_compute_vm: - return cs.advance(1 + 3) // tr_phase_compute_vm$1 success:Bool msg_state_used:Bool account_activated:Bool - && t_Grams.skip(cs) // gas_fees:Grams - && t_Ref_TrComputeInternal1.skip(cs); // ^[ gas_used:(..) .. ] - } - return false; -} - -bool TrComputePhase::validate_skip(vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case tr_phase_compute_skipped: - return cs.advance(1) && t_ComputeSkipReason.validate_skip(cs); - case tr_phase_compute_vm: - return cs.advance(1 + 3) // tr_phase_compute_vm$1 success:Bool msg_state_used:Bool account_activated:Bool - && t_Grams.validate_skip(cs) // gas_fees:Grams - && t_Ref_TrComputeInternal1.validate_skip(cs); // ^[ gas_used:(..) .. ] - } - return false; -} - -const TrComputePhase t_TrComputePhase; - -bool TrActionPhase::skip(vm::CellSlice& cs) const { - return cs.advance(3) // success:Bool valid:Bool no_funds:Bool - && t_AccStatusChange.skip(cs) // status_change:AccStatusChange - && t_Maybe_Grams.skip(cs) // total_fwd_fees:(Maybe Grams) - && t_Maybe_Grams.skip(cs) // total_action_fees:(Maybe Grams) - && cs.advance(32) // result_code:int32 - && Maybe{32}.skip(cs) // result_arg:(Maybe int32) - && cs.advance(16 * 4 + 256) // tot_actions:uint16 spec_actions:uint16 - // skipped_actions:uint16 msgs_created:uint16 - // action_list_hash:uint256 - && t_StorageUsedShort.skip(cs); // tot_msg_size:StorageUsedShort -} - -bool TrActionPhase::validate_skip(vm::CellSlice& cs) const { - return cs.advance(3) // success:Bool valid:Bool no_funds:Bool - && t_AccStatusChange.validate_skip(cs) // status_change:AccStatusChange - && t_Maybe_Grams.validate_skip(cs) // total_fwd_fees:(Maybe Grams) - && t_Maybe_Grams.validate_skip(cs) // total_action_fees:(Maybe Grams) - && cs.advance(32) // result_code:int32 - && Maybe{32}.validate_skip(cs) // result_arg:(Maybe int32) - && cs.advance(16 * 4 + 256) // tot_actions:uint16 spec_actions:uint16 - // skipped_actions:uint16 msgs_created:uint16 - // action_list_hash:uint256 - && t_StorageUsedShort.validate_skip(cs); // tot_msg_size:StorageUsed -} - -const TrActionPhase t_TrActionPhase; - -bool TrBouncePhase::skip(vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case tr_phase_bounce_negfunds: - return cs.advance(2); // tr_phase_bounce_negfunds$00 - case tr_phase_bounce_nofunds: - return cs.advance(2) // tr_phase_bounce_nofunds$01 - && t_StorageUsedShort.skip(cs) // msg_size:StorageUsedShort - && t_Grams.skip(cs); // req_fwd_fees:Grams - case tr_phase_bounce_ok: - return cs.advance(1) // tr_phase_bounce_ok$1 - && t_StorageUsedShort.skip(cs) // msg_size:StorageUsedShort - && t_Grams.skip(cs) // msg_fees:Grams - && t_Grams.skip(cs); // fwd_fees:Grams - } - return false; -} - -bool TrBouncePhase::validate_skip(vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case tr_phase_bounce_negfunds: - return cs.advance(2); // tr_phase_bounce_negfunds$00 - case tr_phase_bounce_nofunds: - return cs.advance(2) // tr_phase_bounce_nofunds$01 - && t_StorageUsedShort.validate_skip(cs) // msg_size:StorageUsedShort - && t_Grams.validate_skip(cs); // req_fwd_fees:Grams - case tr_phase_bounce_ok: - return cs.advance(1) // tr_phase_bounce_ok$1 - && t_StorageUsedShort.validate_skip(cs) // msg_size:StorageUsedShort - && t_Grams.validate_skip(cs) // msg_fees:Grams - && t_Grams.validate_skip(cs); // fwd_fees:Grams - } - return false; -} - -int TrBouncePhase::get_tag(const vm::CellSlice& cs) const { - if (cs.size() == 1) { - return (int)cs.prefetch_ulong(1) == 1 ? tr_phase_bounce_ok : -1; - } - int v = (int)cs.prefetch_ulong(2); - return v == 3 ? tr_phase_bounce_ok : v; -}; - -const TrBouncePhase t_TrBouncePhase; - -bool SplitMergeInfo::skip(vm::CellSlice& cs) const { - // cur_shard_pfx_len:(## 6) acc_split_depth:(##6) this_addr:uint256 sibling_addr:uint256 - return cs.advance(6 + 6 + 256 + 256); -} - -bool SplitMergeInfo::validate_skip(vm::CellSlice& cs) const { - if (!cs.have(6 + 6 + 256 + 256)) { - return false; - } - int cur_pfx_len = (int)cs.fetch_ulong(6); - int split_depth = (int)cs.fetch_ulong(6); - unsigned char this_addr[32], sibling_addr[32]; - if (!cs.fetch_bytes(this_addr, 32) || !cs.fetch_bytes(sibling_addr, 32)) { - return false; - } - // cur_pfx_len < split_depth, addresses match except in bit cur_pfx_len - if (cur_pfx_len >= split_depth) { - return false; - } - sibling_addr[cur_pfx_len >> 3] ^= (unsigned char)(0x80 >> (cur_pfx_len & 7)); - return !memcmp(this_addr, sibling_addr, 32); -} - -const SplitMergeInfo t_SplitMergeInfo; - -bool TransactionDescr::skip(vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case trans_ord: - return cs.advance(4 + 1) // trans_ord$0000 storage_first:Bool - && Maybe{}.skip(cs) // storage_ph:(Maybe TrStoragePhase) - && Maybe{}.skip(cs) // credit_ph:(Maybe TrCreditPhase) - && t_TrComputePhase.skip(cs) // compute_ph:TrComputePhase - && Maybe>{}.skip(cs) // action:(Maybe ^TrActionPhase) - && cs.advance(1) // aborted:Bool - && Maybe{}.skip(cs) // bounce:(Maybe TrBouncePhase) - && cs.advance(1); // destroyed:Bool - case trans_storage: - return cs.advance(4) // trans_storage$0001 - && t_TrStoragePhase.skip(cs); // storage_ph:TrStoragePhase - case trans_tick_tock: - return cs.advance(4) // trans_tick_tock$001 is_tock:Bool - && t_TrStoragePhase.skip(cs) // storage:TrStoragePhase - && t_TrComputePhase.skip(cs) // compute_ph:TrComputePhase - && Maybe>{}.skip(cs) // action:(Maybe ^TrActionPhase) - && cs.advance(2); // aborted:Bool destroyed:Bool - case trans_split_prepare: - return cs.advance(4) // trans_split_prepare$0100 - && t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo - && t_TrComputePhase.skip(cs) // compute_ph:TrComputePhase - && Maybe>{}.skip(cs) // action:(Maybe ^TrActionPhase) - && cs.advance(2); // aborted:Bool destroyed:Bool - case trans_split_install: - return cs.advance(4) // trans_split_install$0101 - && t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo - && t_Ref_Transaction.skip(cs) // prepare_transaction:^Transaction - && cs.advance(1); // installed:Bool - case trans_merge_prepare: - return cs.advance(4) // trans_merge_prepare$0110 - && t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo - && t_TrStoragePhase.skip(cs) // storage_ph:TrStoragePhase - && cs.advance(1); // aborted:Bool - case trans_merge_install: - return cs.advance(4) // trans_merge_install$0111 - && t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo - && t_Ref_Transaction.skip(cs) // prepare_transaction:^Transaction - && Maybe{}.skip(cs) // credit_ph:(Maybe TrCreditPhase) - && Maybe{}.skip(cs) // compute_ph:TrComputePhase - && Maybe>{}.skip(cs) // action:(Maybe ^TrActionPhase) - && cs.advance(2); // aborted:Bool destroyed:Bool - } - return false; -} - -bool TransactionDescr::validate_skip(vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case trans_ord: - return cs.advance(4 + 1) // trans_ord$0000 credit_first:Bool - && Maybe{}.validate_skip(cs) // storage_ph:(Maybe TrStoragePhase) - && Maybe{}.validate_skip(cs) // credit_ph:(Maybe TrCreditPhase) - && t_TrComputePhase.validate_skip(cs) // compute_ph:TrComputePhase - && Maybe>{}.validate_skip(cs) // action:(Maybe ^TrActionPhase) - && cs.advance(1) // aborted:Bool - && Maybe{}.validate_skip(cs) // bounce:(Maybe TrBouncePhase) - && cs.advance(1); // destroyed:Bool - case trans_storage: - return cs.advance(4) // trans_storage$0001 - && t_TrStoragePhase.validate_skip(cs); // storage_ph:TrStoragePhase - case trans_tick_tock: - return cs.advance(4) // trans_tick_tock$001 is_tock:Bool - && t_TrStoragePhase.validate_skip(cs) // storage:TrStoragePhase - && t_TrComputePhase.validate_skip(cs) // compute_ph:TrComputePhase - && Maybe>{}.validate_skip(cs) // action:(Maybe ^TrActionPhase) - && cs.advance(2); // aborted:Bool destroyed:Bool - case trans_split_prepare: - return cs.advance(4) // trans_split_prepare$0100 - && t_SplitMergeInfo.validate_skip(cs) // split_info:SplitMergeInfo - && t_TrComputePhase.validate_skip(cs) // compute_ph:TrComputePhase - && Maybe>{}.validate_skip(cs) // action:(Maybe ^TrActionPhase) - && cs.advance(2); // aborted:Bool destroyed:Bool - case trans_split_install: - return cs.advance(4) // trans_split_install$0101 - && t_SplitMergeInfo.validate_skip(cs) // split_info:SplitMergeInfo - && t_Ref_Transaction.validate_skip(cs) // prepare_transaction:^Transaction - && cs.advance(1); // installed:Bool - case trans_merge_prepare: - return cs.advance(4) // trans_merge_prepare$0110 - && t_SplitMergeInfo.validate_skip(cs) // split_info:SplitMergeInfo - && t_TrStoragePhase.validate_skip(cs) // storage_ph:TrStoragePhase - && cs.advance(1); // aborted:Bool - case trans_merge_install: - return cs.advance(4) // trans_merge_install$0111 - && t_SplitMergeInfo.validate_skip(cs) // split_info:SplitMergeInfo - && t_Ref_Transaction.validate_skip(cs) // prepare_transaction:^Transaction - && Maybe{}.validate_skip(cs) // credit_ph:(Maybe TrCreditPhase) - && Maybe{}.validate_skip(cs) // compute_ph:TrComputePhase - && Maybe>{}.validate_skip(cs) // action:(Maybe ^TrActionPhase) - && cs.advance(2); // aborted:Bool destroyed:Bool - } - return false; -} - -int TransactionDescr::get_tag(const vm::CellSlice& cs) const { - int t = (int)cs.prefetch_ulong(4); - return (t >= 0 && t <= 7) ? (t == 3 ? 2 : t) : -1; -} - -const TransactionDescr t_TransactionDescr; - -bool Transaction::skip(vm::CellSlice& cs) const { - return cs.advance( - 4 + 256 + 64 + 256 + 64 + 32 + - 15) // transaction$0110 account_addr:uint256 lt:uint64 prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32 outmsg_cnt:uint15 - && t_AccountStatus.skip(cs) // orig_status:AccountStatus - && t_AccountStatus.skip(cs) // end_status:AccountStatus - && Maybe>{}.skip(cs) // in_msg:(Maybe ^Message) - && HashmapE{15, t_Ref_Message}.skip(cs) // out_msgs:(HashmapE 15 ^Message) - && t_Grams.skip(cs) // total_fees:Grams - && t_RefCell.skip(cs) // state_update:^(MERKLE_UPDATE Account) - && RefTo{}.skip(cs); // description:^TransactionDescr -} - -bool Transaction::validate_skip(vm::CellSlice& cs) const { - return cs.fetch_ulong(4) == 6 // transaction$0110 - && - cs.advance( - 256 + 64 + 256 + 64 + 32 + - 15) // account_addr:uint256 lt:uint64 prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32 outmsg_cnt:uint15 - && t_AccountStatus.validate_skip(cs) // orig_status:AccountStatus - && t_AccountStatus.validate_skip(cs) // end_status:AccountStatus - && Maybe>{}.validate_skip(cs) // in_msg:(Maybe ^Message) - && HashmapE{15, t_Ref_Message}.validate_skip(cs) // out_msgs:(HashmapE 15 ^Message) - && t_Grams.validate_skip(cs) // total_fees:Grams - && t_RefCell.validate_skip(cs) // FIXME state_update:^(MERKLE_UPDATE Account) - && RefTo{}.validate_skip(cs); // description:^TransactionDescr -} - -bool Transaction::get_total_fees(vm::CellSlice&& cs, td::RefInt256& total_fees) const { - return cs.fetch_ulong(4) == 6 // transaction$0110 - && - cs.advance( - 256 + 64 + 256 + 64 + 32 + - 15) // account_addr:uint256 lt:uint64 prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32 outmsg_cnt:uint15 - && t_AccountStatus.skip(cs) // orig_status:AccountStatus - && t_AccountStatus.skip(cs) // end_status:AccountStatus - && Maybe>{}.skip(cs) // in_msg:(Maybe ^Message) - && HashmapE{15, t_Ref_Message}.skip(cs) // out_msgs:(HashmapE 15 ^Message) - && t_Grams.as_integer_skip_to(cs, total_fees); // total_fees:Grams -} - -const Transaction t_Transaction; -const RefTo t_Ref_Transaction; - -// leaf evaluation for (HashmapAug 64 ^Transaction Grams) -bool Aug_AccountTransactions::eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const { - auto cell_ref = cs.prefetch_ref(); - td::RefInt256 total_fees; - return cell_ref.not_null() && t_Transaction.get_total_fees(vm::load_cell_slice(std::move(cell_ref)), total_fees) && - t_Grams.store_integer_ref(cb, std::move(total_fees)); -} - -const Aug_AccountTransactions aug_AccountTransactions; -const HashmapAug t_AccountTransactions{64, aug_AccountTransactions}; - -const HashUpdate t_HashUpdate; -const RefTo t_Ref_HashUpdate; - -bool AccountBlock::skip(vm::CellSlice& cs) const { - return cs.advance(4 + 256) // acc_trans#4 account_addr:bits256 - && t_AccountTransactions.skip(cs) // transactions:(HashmapAug 64 ^Transaction Grams) - && cs.advance_refs(1); // state_update:^(HASH_UPDATE Account) -} - -bool AccountBlock::validate_skip(vm::CellSlice& cs) const { - return cs.fetch_ulong(4) == 4 // acc_trans#4 - && cs.advance(256) // account_addr:bits256 - && t_AccountTransactions.validate_skip(cs) // transactions:(HashmapAug 64 ^Transaction Grams) - && t_Ref_HashUpdate.validate_skip(cs); // state_update:^(HASH_UPDATE Account) -} - -bool AccountBlock::get_total_fees(vm::CellSlice&& cs, td::RefInt256& total_fees) const { - return cs.advance(4 + 256) // acc_trans#4 account_addr:bits256 - && t_AccountTransactions.extract_extra(cs) // transactions:(HashmapAug 64 ^Transaction Grams) - && t_Grams.as_integer_skip_to(cs, total_fees); -} - -const AccountBlock t_AccountBlock; - -bool Aug_ShardAccountBlocks::eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const { - td::RefInt256 total_fees; - return t_AccountBlock.get_total_fees(std::move(cs), total_fees) && - t_Grams.store_integer_ref(cb, std::move(total_fees)); -} - -const Aug_ShardAccountBlocks aug_ShardAccountBlocks; -const HashmapAugE t_ShardAccountBlocks{256, aug_ShardAccountBlocks}; // (HashmapAugE 256 AccountBlock Grams) - -bool ImportFees::validate_skip(vm::CellSlice& cs) const { - return t_Grams.validate_skip(cs) && t_CurrencyCollection.validate_skip(cs); -} - -bool ImportFees::skip(vm::CellSlice& cs) const { - return t_Grams.skip(cs) && t_CurrencyCollection.skip(cs); -} - -bool ImportFees::add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const { - return t_Grams.add_values(cb, cs1, cs2) && t_CurrencyCollection.add_values(cb, cs1, cs2); -} - -const ImportFees t_ImportFees; - -bool InMsg::skip(vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case msg_import_ext: - return cs.advance(3) // msg_import_ext$000 - && t_Ref_Message.skip(cs) // msg:^Message - && t_Ref_Transaction.skip(cs); // transaction:^Transaction - case msg_import_ihr: - return cs.advance(3) // msg_import_ihr$010 - && t_Ref_Message.skip(cs) // msg:^Message - && t_Ref_Transaction.skip(cs) // transaction:^Transaction - && t_Grams.skip(cs) // ihr_fee:Grams - && t_RefCell.skip(cs); // proof_created:^Cell - case msg_import_imm: - return cs.advance(3) // msg_import_imm$011 - && t_Ref_MsgEnvelope.skip(cs) // in_msg:^MsgEnvelope - && t_Ref_Transaction.skip(cs) // transaction:^Transaction - && t_Grams.skip(cs); // fwd_fee:Grams - case msg_import_fin: - return cs.advance(3) // msg_import_fin$100 - && t_Ref_MsgEnvelope.skip(cs) // in_msg:^MsgEnvelope - && t_Ref_Transaction.skip(cs) // transaction:^Transaction - && t_Grams.skip(cs); // fwd_fee:Grams - case msg_import_tr: - return cs.advance(3) // msg_import_tr$101 - && t_Ref_MsgEnvelope.skip(cs) // in_msg:^MsgEnvelope - && t_Ref_MsgEnvelope.skip(cs) // out_msg:^MsgEnvelope - && t_Grams.skip(cs); // transit_fee:Grams - case msg_discard_fin: - return cs.advance(3) // msg_discard_fin$110 - && t_Ref_MsgEnvelope.skip(cs) // in_msg:^MsgEnvelope - && cs.advance(64) // transaction_id:uint64 - && t_Grams.skip(cs); // fwd_fee:Grams - case msg_discard_tr: - return cs.advance(3) // msg_discard_tr$111 - && t_Ref_MsgEnvelope.skip(cs) // in_msg:^MsgEnvelope - && cs.advance(64) // transaction_id:uint64 - && t_Grams.skip(cs) // fwd_fee:Grams - && t_RefCell.skip(cs); // proof_delivered:^Cell - } - return false; -} - -bool InMsg::validate_skip(vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case msg_import_ext: - return cs.advance(3) // msg_import_ext$000 - && t_Ref_Message.validate_skip(cs) // msg:^Message - && t_Ref_Transaction.validate_skip(cs); // transaction:^Transaction - case msg_import_ihr: - return cs.advance(3) // msg_import_ihr$010 - && t_Ref_Message.validate_skip(cs) // msg:^Message - && t_Ref_Transaction.validate_skip(cs) // transaction:^Transaction - && t_Grams.validate_skip(cs) // ihr_fee:Grams - && t_RefCell.validate_skip(cs); // proof_created:^Cell - case msg_import_imm: - return cs.advance(3) // msg_import_imm$011 - && t_Ref_MsgEnvelope.validate_skip(cs) // in_msg:^MsgEnvelope - && t_Ref_Transaction.validate_skip(cs) // transaction:^Transaction - && t_Grams.validate_skip(cs); // fwd_fee:Grams - case msg_import_fin: - return cs.advance(3) // msg_import_fin$100 - && t_Ref_MsgEnvelope.validate_skip(cs) // in_msg:^MsgEnvelope - && t_Ref_Transaction.validate_skip(cs) // transaction:^Transaction - && t_Grams.validate_skip(cs); // fwd_fee:Grams - case msg_import_tr: - return cs.advance(3) // msg_import_tr$101 - && t_Ref_MsgEnvelope.validate_skip(cs) // in_msg:^MsgEnvelope - && t_Ref_MsgEnvelope.validate_skip(cs) // out_msg:^MsgEnvelope - && t_Grams.validate_skip(cs); // transit_fee:Grams - case msg_discard_fin: - return cs.advance(3) // msg_discard_fin$110 - && t_Ref_MsgEnvelope.validate_skip(cs) // in_msg:^MsgEnvelope - && cs.advance(64) // transaction_id:uint64 - && t_Grams.validate_skip(cs); // fwd_fee:Grams - case msg_discard_tr: - return cs.advance(3) // msg_discard_tr$111 - && t_Ref_MsgEnvelope.validate_skip(cs) // in_msg:^MsgEnvelope - && cs.advance(64) // transaction_id:uint64 - && t_Grams.validate_skip(cs) // fwd_fee:Grams - && t_RefCell.validate_skip(cs); // proof_delivered:^Cell - } - return false; -} - -bool InMsg::get_import_fees(vm::CellBuilder& cb, vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case msg_import_ext: // inbound external message - return t_ImportFees.null_value(cb); // external messages have no value and no import fees - case msg_import_ihr: // IHR-forwarded internal message to its final destination - if (cs.advance(3) && cs.size_refs() >= 3) { - auto msg_cs = load_cell_slice(cs.fetch_ref()); - CommonMsgInfo::Record_int_msg_info msg_info; - td::RefInt256 ihr_fee; - vm::CellBuilder aux; - // sort of Prolog-style in C++ - return t_Message.extract_info(msg_cs) && t_CommonMsgInfo.unpack(msg_cs, msg_info) && - cs.fetch_ref().not_null() && (ihr_fee = t_Grams.as_integer_skip(cs)).not_null() && - cs.fetch_ref().not_null() && !cmp(ihr_fee, t_Grams.as_integer(*msg_info.ihr_fee)) && - cb.append_cellslice_bool(msg_info.ihr_fee) // fees_collected := ihr_fee - && aux.append_cellslice_bool(msg_info.ihr_fee) && t_ExtraCurrencyCollection.null_value(aux) && - t_CurrencyCollection.add_values(cb, aux.as_cellslice_ref().write(), - msg_info.value.write()); // value_imported := ihr_fee + value - } - return false; - case msg_import_imm: // internal message re-imported from this very block - if (cs.advance(3) && cs.size_refs() >= 2) { - return cs.fetch_ref().not_null() && cs.fetch_ref().not_null() && - cb.append_cellslice_bool(t_Grams.fetch(cs)) // fees_collected := fwd_fees - && t_CurrencyCollection.null_value(cb); // value_imported := 0 - } - return false; - case msg_import_fin: // internal message delivered to its final destination in this block - if (cs.advance(3) && cs.size_refs() >= 2) { - auto msg_env_cs = load_cell_slice(cs.fetch_ref()); - MsgEnvelope::Record in_msg; - td::RefInt256 fwd_fee, fwd_fee_remaining, value_grams, ihr_fee; - if (!(t_MsgEnvelope.unpack(msg_env_cs, in_msg) && cs.fetch_ref().not_null() && - t_Grams.as_integer_skip_to(cs, fwd_fee) && - (fwd_fee_remaining = t_Grams.as_integer(in_msg.fwd_fee_remaining)).not_null() && - !(cmp(fwd_fee, fwd_fee_remaining)))) { - return false; - } - auto msg_cs = load_cell_slice(std::move(in_msg.msg)); - CommonMsgInfo::Record_int_msg_info msg_info; - return t_Message.extract_info(msg_cs) && t_CommonMsgInfo.unpack(msg_cs, msg_info) && - cb.append_cellslice_bool(in_msg.fwd_fee_remaining) // fees_collected := fwd_fee_remaining - && t_Grams.as_integer_skip_to(msg_info.value.write(), value_grams) && - (ihr_fee = t_Grams.as_integer(std::move(msg_info.ihr_fee))).not_null() && - t_Grams.store_integer_ref(cb, value_grams + ihr_fee + fwd_fee_remaining) && - cb.append_cellslice_bool( - msg_info.value.write()); // value_imported = msg.value + msg.ihr_fee + fwd_fee_remaining - } - return false; - case msg_import_tr: // transit internal message - if (cs.advance(3) && cs.size_refs() >= 2) { - auto msg_env_cs = load_cell_slice(cs.fetch_ref()); - MsgEnvelope::Record in_msg; - td::RefInt256 transit_fee, fwd_fee_remaining, value_grams, ihr_fee; - if (!(t_MsgEnvelope.unpack(msg_env_cs, in_msg) && cs.fetch_ref().not_null() && - t_Grams.as_integer_skip_to(cs, transit_fee) && - (fwd_fee_remaining = t_Grams.as_integer(in_msg.fwd_fee_remaining)).not_null() && - cmp(transit_fee, fwd_fee_remaining) <= 0)) { - return false; - } - auto msg_cs = load_cell_slice(in_msg.msg); - CommonMsgInfo::Record_int_msg_info msg_info; - return t_Message.extract_info(msg_cs) && t_CommonMsgInfo.unpack(msg_cs, msg_info) && - t_Grams.store_integer_ref(cb, std::move(transit_fee)) // fees_collected := transit_fees - && t_Grams.as_integer_skip_to(msg_info.value.write(), value_grams) && - (ihr_fee = t_Grams.as_integer(std::move(msg_info.ihr_fee))).not_null() && - t_Grams.store_integer_ref(cb, value_grams + ihr_fee + fwd_fee_remaining) && - cb.append_cellslice_bool( - msg_info.value.write()); // value_imported = msg.value + msg.ihr_fee + fwd_fee_remaining - } - return false; - case msg_discard_fin: // internal message discarded at its final destination because of previous IHR delivery - if (cs.advance(3) && cs.size_refs() >= 1) { - Ref fwd_fee; - return cs.fetch_ref().not_null() && cs.advance(64) && (fwd_fee = t_Grams.fetch(cs)).not_null() && - cb.append_cellslice_bool(fwd_fee) // fees_collected := fwd_fee - && cb.append_cellslice_bool(std::move(fwd_fee)) && - t_ExtraCurrencyCollection.null_value(cb); // value_imported := fwd_fee - } - return false; - case msg_discard_tr: // internal message discarded at an intermediate destination - if (cs.advance(3) && cs.size_refs() >= 2) { - Ref fwd_fee; - return cs.fetch_ref().not_null() && cs.advance(64) && (fwd_fee = t_Grams.fetch(cs)).not_null() && - cs.fetch_ref().not_null() && cb.append_cellslice_bool(fwd_fee) // fees_collected := fwd_fee - && cb.append_cellslice_bool(std::move(fwd_fee)) && - t_ExtraCurrencyCollection.null_value(cb); // value_imported := fwd_fee - } - return false; - } - return false; -} - -const InMsg t_InMsg; - -const Aug_InMsgDescr aug_InMsgDescr; -const InMsgDescr t_InMsgDescr; - -bool OutMsg::skip(vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case msg_export_ext: - return cs.advance(3) // msg_export_ext$000 - && t_Ref_Message.skip(cs) // msg:^Message - && t_Ref_Transaction.skip(cs); // transaction:^Transaction - case msg_export_imm: - return cs.advance(3) // msg_export_imm$010 - && t_Ref_MsgEnvelope.skip(cs) // out_msg:^MsgEnvelope - && t_Ref_Transaction.skip(cs) // transaction:^Transaction - && RefTo{}.skip(cs); // reimport:^InMsg - case msg_export_new: - return cs.advance(3) // msg_export_new$001 - && t_Ref_MsgEnvelope.skip(cs) // out_msg:^MsgEnvelope - && t_Ref_Transaction.skip(cs); // transaction:^Transaction - case msg_export_tr: - return cs.advance(3) // msg_export_tr$011 - && t_Ref_MsgEnvelope.skip(cs) // out_msg:^MsgEnvelope - && RefTo{}.skip(cs); // imported:^InMsg - case msg_export_deq_imm: - return cs.advance(3) // msg_export_deq_imm$100 - && t_Ref_MsgEnvelope.skip(cs) // out_msg:^MsgEnvelope - && RefTo{}.skip(cs); // reimport:^InMsg - case msg_export_deq: - return cs.advance(3) // msg_export_deq$110 - && t_Ref_MsgEnvelope.skip(cs) // out_msg:^MsgEnvelope - && cs.advance(64); // import_block_lt:uint64 - case msg_export_tr_req: - return cs.advance(3) // msg_export_tr_req$111 - && t_Ref_MsgEnvelope.skip(cs) // out_msg:^MsgEnvelope - && RefTo{}.skip(cs); // imported:^InMsg - } - return false; -} - -bool OutMsg::validate_skip(vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case msg_export_ext: - return cs.advance(3) // msg_export_ext$000 - && t_Ref_Message.validate_skip(cs) // msg:^Message - && t_Ref_Transaction.validate_skip(cs); // transaction:^Transaction - case msg_export_imm: - return cs.advance(3) // msg_export_imm$010 - && t_Ref_MsgEnvelope.validate_skip(cs) // out_msg:^MsgEnvelope - && t_Ref_Transaction.validate_skip(cs) // transaction:^Transaction - && RefTo{}.validate_skip(cs); // reimport:^InMsg - case msg_export_new: - return cs.advance(3) // msg_export_new$001 - && t_Ref_MsgEnvelope.validate_skip(cs) // out_msg:^MsgEnvelope - && t_Ref_Transaction.validate_skip(cs); // transaction:^Transaction - case msg_export_tr: - return cs.advance(3) // msg_export_tr$011 - && t_Ref_MsgEnvelope.validate_skip(cs) // out_msg:^MsgEnvelope - && RefTo{}.validate_skip(cs); // imported:^InMsg - case msg_export_deq_imm: - return cs.advance(3) // msg_export_deq_imm$100 - && t_Ref_MsgEnvelope.validate_skip(cs) // out_msg:^MsgEnvelope - && RefTo{}.validate_skip(cs); // reimport:^InMsg - case msg_export_deq: - return cs.advance(3) // msg_export_deq$110 - && t_Ref_MsgEnvelope.validate_skip(cs) // out_msg:^MsgEnvelope - && cs.advance(64); // import_block_lt:uint64 - case msg_export_tr_req: - return cs.advance(3) // msg_export_tr_req$111 - && t_Ref_MsgEnvelope.validate_skip(cs) // out_msg:^MsgEnvelope - && RefTo{}.validate_skip(cs); // imported:^InMsg - } - return false; -} - -bool OutMsg::get_export_value(vm::CellBuilder& cb, vm::CellSlice& cs) const { - switch (get_tag(cs)) { - case msg_export_ext: // external outbound message carries no value - if (cs.have(3, 2)) { - return t_CurrencyCollection.null_value(cb); - } - return false; - case msg_export_imm: // outbound internal message delivered in this very block, no value exported - return cs.have(3, 3) && t_CurrencyCollection.null_value(cb); - case msg_export_deq_imm: // dequeuing record for outbound message delivered in this very block, no value exported - return cs.have(3, 2) && t_CurrencyCollection.null_value(cb); - case msg_export_deq: // dequeueing record for outbound message, no exported value - return cs.have(3, 1) && t_CurrencyCollection.null_value(cb); - case msg_export_new: // newly-generated outbound internal message, queued - case msg_export_tr: // transit internal message, queued - case msg_export_tr_req: // transit internal message, re-queued from this shardchain - if (cs.advance(3) && cs.size_refs() >= 2) { - auto msg_env_cs = load_cell_slice(cs.fetch_ref()); - MsgEnvelope::Record out_msg; - if (!(cs.fetch_ref().not_null() && t_MsgEnvelope.unpack(msg_env_cs, out_msg))) { - return false; - } - auto msg_cs = load_cell_slice(std::move(out_msg.msg)); - CommonMsgInfo::Record_int_msg_info msg_info; - td::RefInt256 value_grams, ihr_fee, fwd_fee_remaining; - return t_Message.extract_info(msg_cs) && t_CommonMsgInfo.unpack(msg_cs, msg_info) && - (value_grams = t_Grams.as_integer_skip(msg_info.value.write())).not_null() && - (ihr_fee = t_Grams.as_integer(std::move(msg_info.ihr_fee))).not_null() && - (fwd_fee_remaining = t_Grams.as_integer(out_msg.fwd_fee_remaining)).not_null() && - t_Grams.store_integer_ref(cb, value_grams + ihr_fee + fwd_fee_remaining) && - cb.append_cellslice_bool(std::move(msg_info.value)); - // exported value = msg.value + msg.ihr_fee + fwd_fee_remaining - } - return false; - } - return false; -} - -bool OutMsg::get_created_lt(vm::CellSlice& cs, unsigned long long& created_lt) const { - switch (get_tag(cs)) { - case msg_export_ext: - if (cs.have(3, 1)) { - auto msg_cs = load_cell_slice(cs.prefetch_ref()); - return t_Message.get_created_lt(msg_cs, created_lt); - } else { - return false; - } - case msg_export_imm: - case msg_export_new: - case msg_export_tr: - case msg_export_deq: - case msg_export_deq_imm: - case msg_export_tr_req: - if (cs.have(3, 1)) { - auto out_msg_cs = load_cell_slice(cs.prefetch_ref()); - return t_MsgEnvelope.get_created_lt(out_msg_cs, created_lt); - } else { - return false; - } - } - return false; -} - -const OutMsg t_OutMsg; - -const Aug_OutMsgDescr aug_OutMsgDescr; -const OutMsgDescr t_OutMsgDescr; - -bool EnqueuedMsg::validate_skip(vm::CellSlice& cs) const { - return cs.advance(64) && t_Ref_MsgEnvelope.validate_skip(cs); -} - -const EnqueuedMsg t_EnqueuedMsg; - -bool Aug_OutMsgQueue::eval_fork(vm::CellBuilder& cb, vm::CellSlice& left_cs, vm::CellSlice& right_cs) const { - unsigned long long x, y; - return left_cs.fetch_ulong_bool(64, x) && right_cs.fetch_ulong_bool(64, y) && - cb.store_ulong_rchk_bool(std::min(x, y), 64); -} - -bool Aug_OutMsgQueue::eval_empty(vm::CellBuilder& cb) const { - return cb.store_long_bool(0, 64); -} - -bool Aug_OutMsgQueue::eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const { - Ref msg_env; - unsigned long long created_lt; - return cs.fetch_ref_to(msg_env) && t_MsgEnvelope.get_created_lt(load_cell_slice(std::move(msg_env)), created_lt) && - cb.store_ulong_rchk_bool(created_lt, 64); -} - -const Aug_OutMsgQueue aug_OutMsgQueue; -const OutMsgQueue t_OutMsgQueue; - -const ProcessedUpto t_ProcessedUpto; -const HashmapE t_ProcessedInfo{96, t_ProcessedUpto}; -const HashmapE t_IhrPendingInfo{256, t_uint128}; - -// _ out_queue:OutMsgQueue proc_info:ProcessedInfo = OutMsgQueueInfo; -bool OutMsgQueueInfo::skip(vm::CellSlice& cs) const { - return t_OutMsgQueue.skip(cs) && t_ProcessedInfo.skip(cs) && t_IhrPendingInfo.skip(cs); -} - -bool OutMsgQueueInfo::validate_skip(vm::CellSlice& cs) const { - return t_OutMsgQueue.validate_skip(cs) && t_ProcessedInfo.validate_skip(cs) && t_IhrPendingInfo.validate_skip(cs); -} - -const OutMsgQueueInfo t_OutMsgQueueInfo; -const RefTo t_Ref_OutMsgQueueInfo; - -const ExtBlkRef t_ExtBlkRef; -const BlkMasterInfo t_BlkMasterInfo; - -bool ShardIdent::validate_skip(vm::CellSlice& cs) const { - int shard_pfx_len, workchain_id; - unsigned long long shard_pfx; - if (cs.fetch_ulong(2) == 0 && cs.fetch_uint_to(6, shard_pfx_len) && cs.fetch_int_to(32, workchain_id) && - workchain_id != ton::workchainInvalid && cs.fetch_uint_to(64, shard_pfx)) { - auto pow2 = (1ULL << (63 - shard_pfx_len)); - if (!(shard_pfx & (pow2 - 1))) { - return true; - } - } - return false; -} - -bool ShardIdent::Record::check() const { - return workchain_id != ton::workchainInvalid && !(shard_prefix & ((1ULL << (63 - shard_pfx_bits)) - 1)); -} - -bool ShardIdent::unpack(vm::CellSlice& cs, ShardIdent::Record& data) const { - if (cs.fetch_ulong(2) == 0 && cs.fetch_uint_to(6, data.shard_pfx_bits) && cs.fetch_int_to(32, data.workchain_id) && - cs.fetch_uint_to(64, data.shard_prefix) && data.check()) { - return true; - } else { - data.invalidate(); - return false; - } -} - -bool ShardIdent::pack(vm::CellBuilder& cb, const Record& data) const { - return data.check() && cb.store_ulong_rchk_bool(0, 2) && cb.store_ulong_rchk_bool(data.shard_pfx_bits, 6) && - cb.store_long_rchk_bool(data.workchain_id, 32) && cb.store_ulong_rchk_bool(data.shard_prefix, 64); -} - -bool ShardIdent::unpack(vm::CellSlice& cs, ton::WorkchainId& workchain, ton::ShardId& shard) const { - int bits; - unsigned long long pow2; - auto assign = [](auto& a, auto b) { return a = b; }; - auto assign_or = [](auto& a, auto b) { return a |= b; }; - return cs.fetch_ulong(2) == 0 // shard_ident$00 - && cs.fetch_uint_leq(60, bits) // shard_pfx_bits:(#<= 60) - && assign(pow2, (1ULL << (63 - bits))) // (power) - && cs.fetch_int_to(32, workchain) // workchain_id:int32 - && cs.fetch_uint_to(64, shard) // shard_prefix:uint64 - && workchain != ton::workchainInvalid && !(shard & (2 * pow2 - 1)) && assign_or(shard, pow2); -} - -bool ShardIdent::unpack(vm::CellSlice& cs, ton::ShardIdFull& data) const { - return unpack(cs, data.workchain, data.shard); -} - -bool ShardIdent::pack(vm::CellBuilder& cb, ton::WorkchainId workchain, ton::ShardId shard) const { - int bits = ton::shard_prefix_length(shard); - return workchain != ton::workchainInvalid // check workchain - && shard // check shard - && cb.store_long_bool(0, 2) // shard_ident$00 - && cb.store_uint_leq(60, bits) // shard_pfx_bits:(#<= 60) - && cb.store_long_bool(workchain, 32) // workchain_id:int32 - && cb.store_long_bool(shard & (shard - 1), 64); // shard_prefix:uint64 -} - -bool ShardIdent::pack(vm::CellBuilder& cb, ton::ShardIdFull data) const { - return pack(cb, data.workchain, data.shard); -} - -const ShardIdent t_ShardIdent; - -bool BlockIdExt::validate_skip(vm::CellSlice& cs) const { - return t_ShardIdent.validate_skip(cs) && cs.advance(32 + 256 * 2); -} - -bool BlockIdExt::unpack(vm::CellSlice& cs, ton::BlockIdExt& data) const { - return t_ShardIdent.unpack(cs, data.id.workchain, data.id.shard) // block_id_ext$_ shard_id:ShardIdent - && cs.fetch_uint_to(32, data.id.seqno) // seq_no:uint32 - && cs.fetch_bits_to(data.root_hash) // root_hash:bits256 - && cs.fetch_bits_to(data.file_hash); // file_hash:bits256 -} - -bool BlockIdExt::pack(vm::CellBuilder& cb, const ton::BlockIdExt& data) const { - return t_ShardIdent.pack(cb, data.id.workchain, data.id.shard) // block_id_ext$_ shard_id:ShardIdent - && cb.store_long_bool(data.id.seqno, 32) // seq_no:uint32 - && cb.store_bits_bool(data.root_hash) // root_hash:bits256 - && cb.store_bits_bool(data.file_hash); // file_hash:bits256 -} - -const BlockIdExt t_BlockIdExt; - -bool ShardState::skip(vm::CellSlice& cs) const { - return get_tag(cs) == shard_state && cs.advance(64) // shard_state#9023afe1 blockchain_id:int32 - && t_ShardIdent.skip(cs) // shard_id:ShardIdent - && cs.advance(32 + 32 + 32 + 64 + - 32) // seq_no:int32 vert_seq_no:# gen_utime:uint32 gen_lt:uint64 min_ref_mc_seqno:uint32 - && t_Ref_OutMsgQueueInfo.skip(cs) // out_msg_queue_info:^OutMsgQueueInfo - && cs.advance(1) // before_split:Bool - && t_ShardAccounts.skip(cs) // accounts:ShardAccounts - && - cs.advance_refs( - 1) // ^[ total_balance:CurrencyCollection total_validator_fees:CurrencyCollection libraries:(HashmapE 256 LibDescr) master_ref:(Maybe BlkMasterInfo) ] - && Maybe>{}.skip(cs); // custom:(Maybe ^McStateExtra) -} - -bool ShardState::validate_skip(vm::CellSlice& cs) const { - int seq_no; - return get_tag(cs) == shard_state && cs.advance(64) // shard_state#9023afde blockchain_id:int32 - && t_ShardIdent.validate_skip(cs) // shard_id:ShardIdent - && cs.fetch_int_to(32, seq_no) // seq_no:int32 - && seq_no >= -1 // { seq_no >= -1 } - && cs.advance(32 + 32 + 64 + 32) // vert_seq_no:# gen_utime:uint32 gen_lt:uint64 min_ref_mc_seqno:uint32 - && t_Ref_OutMsgQueueInfo.validate_skip(cs) // out_msg_queue_info:^OutMsgQueueInfo - && cs.advance(1) // before_split:Bool - && t_ShardAccounts.validate_skip(cs) // accounts:ShardAccounts - && - t_ShardState_aux.validate_skip_ref( - cs) // ^[ total_balance:CurrencyCollection total_validator_fees:CurrencyCollection libraries:(HashmapE 256 LibDescr) master_ref:(Maybe BlkMasterInfo) ] - && Maybe>{}.validate_skip(cs); // custom:(Maybe ^McStateExtra) -} - -const ShardState t_ShardState; - -bool ShardState_aux::skip(vm::CellSlice& cs) const { - return cs.advance(128) // overload_history:uint64 underload_history:uint64 - && t_CurrencyCollection.skip(cs) // total_balance:CurrencyCollection - && t_CurrencyCollection.skip(cs) // total_validator_fees:CurrencyCollection - && HashmapE{256, t_LibDescr}.skip(cs) // libraries:(HashmapE 256 LibDescr) - && Maybe{}.skip(cs); // master_ref:(Maybe BlkMasterInfo) -} - -bool ShardState_aux::validate_skip(vm::CellSlice& cs) const { - return cs.advance(128) // overload_history:uint64 underload_history:uint64 - && t_CurrencyCollection.validate_skip(cs) // total_balance:CurrencyCollection - && t_CurrencyCollection.validate_skip(cs) // total_validator_fees:CurrencyCollection - && HashmapE{256, t_LibDescr}.validate_skip(cs) // libraries:(HashmapE 256 LibDescr) - && Maybe{}.validate_skip(cs); // master_ref:(Maybe BlkMasterInfo) -} - -const ShardState_aux t_ShardState_aux; - -bool LibDescr::skip(vm::CellSlice& cs) const { - return cs.advance(2) // shared_lib_descr$00 - && cs.fetch_ref().not_null() // lib:^Cell - && Hashmap{256, t_True}.skip(cs); // publishers:(Hashmap 256 False) -} - -bool LibDescr::validate_skip(vm::CellSlice& cs) const { - return get_tag(cs) == shared_lib_descr && cs.advance(2) // shared_lib_descr$00 - && cs.fetch_ref().not_null() // lib:^Cell - && Hashmap{256, t_True}.validate_skip(cs); // publishers:(Hashmap 256 False) -} - -const LibDescr t_LibDescr; - -bool BlkPrevInfo::skip(vm::CellSlice& cs) const { - return t_ExtBlkRef.skip(cs) // prev_blk_info$_ {merged:#} prev:ExtBlkRef - && (!merged || t_ExtBlkRef.skip(cs)); // prev_alt:merged?ExtBlkRef -} - -bool BlkPrevInfo::validate_skip(vm::CellSlice& cs) const { - return t_ExtBlkRef.validate_skip(cs) // prev_blk_info$_ {merged:#} prev:ExtBlkRef - && (!merged || t_ExtBlkRef.validate_skip(cs)); // prev_alt:merged?ExtBlkRef -} - -const BlkPrevInfo t_BlkPrevInfo_0{0}; - -bool McStateExtra::skip(vm::CellSlice& cs) const { - return block::gen::t_McStateExtra.skip(cs); -} - -bool McStateExtra::validate_skip(vm::CellSlice& cs) const { - return block::gen::t_McStateExtra.validate_skip(cs); // FIXME -} - -const McStateExtra t_McStateExtra; - -const KeyExtBlkRef t_KeyExtBlkRef; - -bool KeyMaxLt::add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const { - bool key1, key2; - unsigned long long lt1, lt2; - return cs1.fetch_bool_to(key1) && cs1.fetch_ulong_bool(64, lt1) // cs1 => _ key:Bool max_end_lt:uint64 = KeyMaxLt; - && cs2.fetch_bool_to(key2) && cs2.fetch_ulong_bool(64, lt2) // cs2 => _ key:Bool max_end_lt:uint64 = KeyMaxLt; - && cb.store_bool_bool(key1 | key2) && cb.store_long_bool(std::max(lt1, lt2), 64); -} - -const KeyMaxLt t_KeyMaxLt; - -bool Aug_OldMcBlocksInfo::eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const { - return cs.have(65) && cb.append_bitslice(cs.prefetch_bits(65)); // copy first 1+64 bits -}; - -const Aug_OldMcBlocksInfo aug_OldMcBlocksInfo; - -} // namespace tlb - -/* - * - * Other block-related functions - * - */ - -bool store_UInt7(vm::CellBuilder& cb, unsigned long long value) { - return block::tlb::t_VarUInteger_7.store_long(cb, (long long)value); -} - -bool store_UInt7(vm::CellBuilder& cb, unsigned long long value1, unsigned long long value2) { - return store_UInt7(cb, value1) && store_UInt7(cb, value2); -} - -bool store_Maybe_Grams(vm::CellBuilder& cb, td::RefInt256 value) { - if (value.is_null()) { - return cb.store_long_bool(0, 1); - } else { - return cb.store_long_bool(1, 1) && block::tlb::t_Grams.store_integer_ref(cb, std::move(value)); - } -} - -bool store_Maybe_Grams_nz(vm::CellBuilder& cb, td::RefInt256 value) { - if (value.is_null() || !value->sgn()) { - return cb.store_long_bool(0, 1); - } else { - return cb.store_long_bool(1, 1) && block::tlb::t_Grams.store_integer_ref(cb, std::move(value)); - } -} - -bool store_CurrencyCollection(vm::CellBuilder& cb, td::RefInt256 value, Ref extra) { - return block::tlb::t_CurrencyCollection.pack_special(cb, std::move(value), std::move(extra)); -} - -bool fetch_CurrencyCollection(vm::CellSlice& cs, td::RefInt256& value, Ref& extra) { - return block::tlb::t_CurrencyCollection.unpack_special(cs, value, extra); -} - -bool unpack_CurrencyCollection(Ref csr, td::RefInt256& value, Ref& extra) { - if (csr.is_null()) { - return false; - } else if (csr->is_unique()) { - return block::tlb::t_CurrencyCollection.unpack_special(csr.write(), value, extra); - } else { - vm::CellSlice cs{*csr}; - return block::tlb::t_CurrencyCollection.unpack_special(cs, value, extra); - } -} - -bool check_one_library(Ref cs_ref, td::ConstBitPtr key, int n) { - assert(n == 256); - if (cs_ref->size_ext() != 0x10001) { - return false; - } - Ref cell = cs_ref->prefetch_ref(); - const auto& cell_hash = cell->get_hash(); - return !td::bitstring::bits_memcmp(cell_hash.bits(), key, n); -} - -bool valid_library_collection(Ref cell, bool catch_errors) { - if (cell.is_null()) { - return true; - } - if (!catch_errors) { - vm::Dictionary dict{std::move(cell), 256}; - return dict.check_for_each(check_one_library); - } - try { - vm::Dictionary dict{std::move(cell), 256}; - return dict.check_for_each(check_one_library); - } catch (vm::VmError&) { - return false; - } -} - -bool check_one_config_param(Ref cs_ref, td::ConstBitPtr key, td::ConstBitPtr addr, bool relax_par0) { - if (cs_ref->size_ext() != 0x10000) { - return false; - } - Ref cell = cs_ref->prefetch_ref(); - int idx = (int)key.get_int(32); - if (!idx) { - auto cs = load_cell_slice(std::move(cell)); - return cs.size_ext() == 256 && (relax_par0 || cs.fetch_bits(256) == addr); - } else if (idx < 0) { - return true; - } - bool ok = block::gen::ConfigParam{idx}.validate_ref(std::move(cell)); - if (!ok) { - LOG(ERROR) << "configuration parameter #" << idx << " is invalid"; - } - return ok; -} - -const int mandatory_config_params[] = {18, 20, 21, 22, 23, 24, 25, 28, 34}; +const int mandatory_config_params[] = {18, 20, 21, 22, 23, 24, 25, 28, 34}; bool valid_config_data(Ref cell, const td::BitArray<256>& addr, bool catch_errors, bool relax_par0) { using namespace std::placeholders; @@ -2678,7 +1415,8 @@ bool sub_extra_currency(Ref extra1, Ref extra2, Ref= 96) { @@ -2692,6 +1430,12 @@ ton::AccountIdPrefixFull interpolate_addr(ton::AccountIdPrefixFull src, ton::Acc } } +bool interpolate_addr_to(const ton::AccountIdPrefixFull& src, const ton::AccountIdPrefixFull& dest, int d, + ton::AccountIdPrefixFull& res) { + res = interpolate_addr(src, dest, d); + return true; +} + // result: (transit_addr_dest_bits, nh_addr_dest_bits) std::pair perform_hypercube_routing(ton::AccountIdPrefixFull src, ton::AccountIdPrefixFull dest, ton::ShardIdFull cur, int used_dest_bits) { @@ -2699,15 +1443,16 @@ std::pair perform_hypercube_routing(ton::AccountIdPrefixFull src, ton: if (!ton::shard_contains(cur, transit)) { return {-1, -1}; } + if (transit.account_id_prefix == dest.account_id_prefix || ton::shard_contains(cur, dest)) { + // if destination is already reached, or is in this shard, set cur:=next_hop:=dest + return {96, 96}; + } if (transit.workchain == ton::masterchainId || dest.workchain == ton::masterchainId) { - return {0, 96}; // route messages to/from masterchain directly + return {used_dest_bits, 96}; // route messages to/from masterchain directly } if (transit.workchain != dest.workchain) { return {used_dest_bits, 32}; } - if (transit.account_id_prefix == dest.account_id_prefix || ton::shard_contains(cur, dest)) { - return {96, 96}; - } unsigned long long x = cur.shard & (cur.shard - 1), y = cur.shard | (cur.shard - 1); unsigned long long t = transit.account_id_prefix, q = dest.account_id_prefix ^ t; int i = (td::count_leading_zeroes64(q) & -4); // top i bits match, next 4 bits differ @@ -2720,6 +1465,21 @@ std::pair perform_hypercube_routing(ton::AccountIdPrefixFull src, ton: return {28 + i, 32 + i}; } +bool compute_out_msg_queue_key(Ref msg_env, td::BitArray<352>& key) { + block::tlb::MsgEnvelope::Record_std env; + block::gen::CommonMsgInfo::Record_int_msg_info info; + if (!(tlb::unpack_cell(msg_env, env) && tlb::unpack_cell_inexact(env.msg, info))) { + return false; + } + auto src_prefix = block::tlb::t_MsgAddressInt.get_prefix(std::move(info.src)); + auto dest_prefix = block::tlb::t_MsgAddressInt.get_prefix(std::move(info.dest)); + auto next_hop = interpolate_addr(src_prefix, dest_prefix, env.next_addr); + key.bits().store_int(next_hop.workchain, 32); + (key.bits() + 32).store_int(next_hop.account_id_prefix, 64); + (key.bits() + 96).copy_from(env.msg->get_hash().bits(), 256); + return true; +} + bool unpack_block_prev_blk(Ref block_root, const ton::BlockIdExt& id, std::vector& prev, ton::BlockIdExt& mc_blkid, bool& after_split, ton::BlockIdExt* fetch_blkid) { return unpack_block_prev_blk_ext(std::move(block_root), id, prev, mc_blkid, after_split, fetch_blkid).is_ok(); @@ -2834,18 +1594,8 @@ bool get_old_mc_block_id(vm::AugmentedDictionary& prev_blocks_dict, ton::BlockSe bool unpack_old_mc_block_id(Ref old_blk_info, ton::BlockSeqno seqno, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt) { - block::gen::ExtBlkRef::Record data; - if (!(old_blk_info.not_null() && old_blk_info.write().advance(1) && - ::tlb::csr_unpack(std::move(old_blk_info), data) && data.seq_no == seqno)) { - return false; - } - blkid.id = ton::BlockId{ton::masterchainId, ton::shardIdAll, seqno}; - blkid.root_hash = data.root_hash; - blkid.file_hash = data.file_hash; - if (end_lt) { - *end_lt = data.end_lt; - } - return true; + return old_blk_info.not_null() && old_blk_info.write().advance(1) && + block::tlb::t_ExtBlkRef.unpack(std::move(old_blk_info), blkid, end_lt) && blkid.seqno() == seqno; } bool check_old_mc_block_id(vm::AugmentedDictionary* prev_blocks_dict, const ton::BlockIdExt& blkid) { @@ -2856,13 +1606,9 @@ bool check_old_mc_block_id(vm::AugmentedDictionary& prev_blocks_dict, const ton: if (!blkid.id.is_masterchain_ext()) { return false; } - auto val = prev_blocks_dict.lookup(td::BitArray<32>{blkid.id.seqno}); - block::gen::ExtBlkRef::Record data; - if (!(val.not_null() && val.write().advance(1) && ::tlb::csr_unpack(std::move(val), data) && - data.seq_no == blkid.id.seqno)) { - return false; - } - return blkid.root_hash == data.root_hash && blkid.file_hash == data.file_hash; + ton::BlockIdExt old_blkid; + return unpack_old_mc_block_id(prev_blocks_dict.lookup(td::BitArray<32>{blkid.id.seqno}), blkid.id.seqno, old_blkid) && + old_blkid == blkid; } td::Result> get_block_transaction(Ref block_root, ton::WorkchainId workchain, @@ -2907,4 +1653,56 @@ td::Result> get_block_transaction_try(Ref block_root, to } } +bool get_transaction_in_msg(Ref trans_ref, Ref& in_msg) { + block::gen::Transaction::Record trans; + if (!tlb::unpack_cell(std::move(trans_ref), trans)) { + return false; + } else { + in_msg = trans.in_msg->prefetch_ref(); + return true; + } +} + +bool is_transaction_in_msg(Ref trans_ref, Ref msg) { + Ref imsg; + return get_transaction_in_msg(std::move(trans_ref), imsg) && imsg.not_null() == msg.not_null() && + (imsg.is_null() || imsg->get_hash() == msg->get_hash()); +} + +bool is_transaction_out_msg(Ref trans_ref, Ref msg) { + block::gen::Transaction::Record trans; + vm::CellSlice cs; + unsigned long long created_lt; + if (!(trans_ref.not_null() && msg.not_null() && tlb::unpack_cell(std::move(trans_ref), trans) && cs.load_ord(msg) && + block::tlb::t_CommonMsgInfo.get_created_lt(cs, created_lt))) { + return false; + } + if (created_lt <= trans.lt || created_lt > trans.lt + trans.outmsg_cnt) { + return false; + } + try { + auto o_msg = + vm::Dictionary{trans.out_msgs, 15}.lookup_ref(td::BitArray<15>{(long long)(created_lt - trans.lt - 1)}); + return o_msg.not_null() && o_msg->get_hash() == msg->get_hash(); + } catch (vm::VmError& err) { + return false; + } +} + +// transaction$0110 account_addr:bits256 lt:uint64 +bool get_transaction_id(Ref trans_ref, ton::StdSmcAddress& account_addr, ton::LogicalTime& lt) { + if (trans_ref.is_null()) { + return false; + } + vm::CellSlice cs{vm::NoVmOrd(), trans_ref}; + return cs.fetch_ulong(4) == 6 // transaction$0110 + && cs.fetch_bits_to(account_addr) // account_addr:bits256 + && cs.fetch_uint_to(64, lt); // lt:uint64 +} + +bool get_transaction_owner(Ref trans_ref, ton::StdSmcAddress& addr) { + ton::LogicalTime lt; + return get_transaction_id(std::move(trans_ref), addr, lt); +} + } // namespace block diff --git a/ton-test-liteclient-full/lite-client/crypto/block/block.h b/ton-test-liteclient-full/lite-client/crypto/block/block.h index 23eb14e..f90ad11 100644 --- a/ton-test-liteclient-full/lite-client/crypto/block/block.h +++ b/ton-test-liteclient-full/lite-client/crypto/block/block.h @@ -46,6 +46,7 @@ struct StdAddress { static td::Result parse(td::Slice acc_string); }; + inline td::StringBuilder& operator<<(td::StringBuilder& sb, const StdAddress& addr) { return sb << addr.workchain << " : " << addr.addr.to_hex(); } @@ -150,15 +151,20 @@ struct MsgProcessedUptoCollection { bool is_valid() const { return valid; } - bool insert(ton::LogicalTime last_proc_lt, td::ConstBitPtr last_proc_hash, ton::BlockSeqno mc_seqno); - bool insert_infty(ton::BlockSeqno mc_seqno); + bool insert(ton::BlockSeqno mc_seqno, ton::LogicalTime last_proc_lt, td::ConstBitPtr last_proc_hash); + bool insert_infty(ton::BlockSeqno mc_seqno, ton::LogicalTime last_proc_lt = ~0ULL); bool compactify(); bool pack(vm::CellBuilder& cb); + bool is_reduced() const; + bool contains(const MsgProcessedUpto& other) const; + bool contains(const MsgProcessedUptoCollection& other) const; + const MsgProcessedUpto* is_simple_update_of(const MsgProcessedUptoCollection& other, bool& ok) const; ton::BlockSeqno min_mc_seqno() const; bool split(ton::ShardIdFull new_owner); bool combine_with(const MsgProcessedUptoCollection& other); // NB: this is for checking whether we have already imported an internal message bool already_processed(const EnqueuedMsgDescr& msg) const; + bool for_each_mcseqno(std::function) const; }; struct ParamLimits { @@ -247,990 +253,158 @@ struct BlockLimitStatus { } }; -namespace tlb { -using namespace ::tlb; - -struct Anycast final : TLB { - int get_size(const vm::CellSlice& cs) const override { - return cs.have(5) ? 5 + (int)cs.prefetch_ulong(5) : -1; - } - bool skip_get_depth(vm::CellSlice& cs, int& depth) const { - return cs.fetch_uint_leq(30, depth) && cs.advance(depth); - } -}; - -extern const Anycast t_Anycast; - -struct Maybe_Anycast final : public Maybe { - bool skip_get_depth(vm::CellSlice& cs, int& depth) const; -}; - -extern const Maybe_Anycast t_Maybe_Anycast; - -struct VarUInteger final : TLB_Complex { - int n, ln; - VarUInteger(int _n) : n(_n) { - ln = 32 - td::count_leading_zeroes32(n - 1); - } - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; - unsigned long long as_uint(const vm::CellSlice& cs) const override; - bool null_value(vm::CellBuilder& cb) const override { - return cb.store_zeroes_bool(ln); - } - bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override; - unsigned precompute_integer_size(const td::BigInt256& value) const; - unsigned precompute_integer_size(td::RefInt256 value) const; -}; - -extern const VarUInteger t_VarUInteger_3, t_VarUInteger_7, t_VarUInteger_16, t_VarUInteger_32; - -struct VarUIntegerPos final : TLB_Complex { - int n, ln; - VarUIntegerPos(int _n) : n(_n) { - ln = 32 - td::count_leading_zeroes32(n - 1); - } - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; - unsigned long long as_uint(const vm::CellSlice& cs) const override; - bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override; -}; - -extern const VarUIntegerPos t_VarUIntegerPos_16, t_VarUIntegerPos_32; - -struct VarInteger final : TLB_Complex { - int n, ln; - VarInteger(int _n) : n(_n) { - ln = 32 - td::count_leading_zeroes32(n - 1); - } - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; - long long as_int(const vm::CellSlice& cs) const override; - bool null_value(vm::CellBuilder& cb) const override { - return cb.store_zeroes_bool(ln); - } - bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override; -}; - -struct VarIntegerNz final : TLB_Complex { - int n, ln; - VarIntegerNz(int _n) : n(_n) { - ln = 32 - td::count_leading_zeroes32(n - 1); - } - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; - long long as_int(const vm::CellSlice& cs) const override; - bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override; -}; - -struct Unary final : TLB { - int get_size(const vm::CellSlice& cs) const override { - return cs.count_leading(1) + 1; - } - bool validate_skip(vm::CellSlice& cs, int& n) const { - return cs.advance((n = cs.count_leading(1)) + 1); - } - bool skip(vm::CellSlice& cs, int& n) const { - return validate_skip(cs, n); - } - bool validate_skip(vm::CellSlice& cs) const override { - return cs.advance(get_size(cs)); - } - bool skip(vm::CellSlice& cs) const override { - return validate_skip(cs); - } - bool validate(const vm::CellSlice& cs) const override { - return cs.have(get_size(cs)); - } -}; - -extern const Unary t_Unary; - -struct HmLabel final : TLB_Complex { - enum { hml_short = 0, hml_long = 2, hml_same = 3 }; - int m; // max size - HmLabel(int _m) : m(_m) { - } - bool validate_skip(vm::CellSlice& cs, int& n) const; - bool skip(vm::CellSlice& cs, int& n) const { - return validate_skip(cs, n); - } - bool skip(vm::CellSlice& cs) const override { - int n; - return skip(cs, n); - } - bool validate_skip(vm::CellSlice& cs) const override { - int n; - return validate_skip(cs, n); - } - int get_tag(const vm::CellSlice& cs) const override; -}; - -struct Hashmap final : TLB_Complex { - const TLB& value_type; - int n; - Hashmap(int _n, const TLB& _val_type) : value_type(_val_type), n(_n) { - } - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; -}; - -struct HashmapNode final : TLB_Complex { - enum { hmn_leaf = 0, hmn_fork = 1 }; - const TLB& value_type; - int n; - HashmapNode(int _n, const TLB& _val_type) : value_type(_val_type), n(_n) { - } - int get_size(const vm::CellSlice& cs) const override; - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override { - return n > 0 ? hmn_fork : n; - } -}; - -struct HashmapE final : TLB { - enum { hme_empty = 0, hme_root = 1 }; - Hashmap root_type; - HashmapE(int _n, const TLB& _val_type) : root_type(_n, _val_type) { - } - int get_size(const vm::CellSlice& cs) const override; - bool validate(const vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override { - return (int)cs.prefetch_ulong(1); - } - bool null_value(vm::CellBuilder& cb) const override { - return cb.store_zeroes_bool(1); - } - bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override; - int sub_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override; - bool add_values_ref(Ref& res, Ref arg1, Ref arg2) const; - int sub_values_ref(Ref& res, Ref arg1, Ref arg2) const; - bool store_ref(vm::CellBuilder& cb, Ref arg) const; -}; - -struct AugmentationCheckData : vm::dict::AugmentationData { - const TLB& value_type; - const TLB& extra_type; - AugmentationCheckData(const TLB& val_type, const TLB& ex_type) : value_type(val_type), extra_type(ex_type) { - } - bool skip_extra(vm::CellSlice& cs) const override { - return extra_type.skip(cs); - } - bool eval_fork(vm::CellBuilder& cb, vm::CellSlice& left_cs, vm::CellSlice& right_cs) const override { - return extra_type.add_values(cb, left_cs, right_cs); - } - bool eval_empty(vm::CellBuilder& cb) const override { - return extra_type.null_value(cb); - } -}; - -struct HashmapAug final : TLB_Complex { - const AugmentationCheckData& aug; - int n; - HashmapAug(int _n, const AugmentationCheckData& _aug) : aug(_aug), n(_n) { - } - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - bool extract_extra(vm::CellSlice& cs) const; -}; - -struct HashmapAugNode final : TLB_Complex { - enum { ahmn_leaf = 0, ahmn_fork = 1 }; - const AugmentationCheckData& aug; - int n; - HashmapAugNode(int _n, const AugmentationCheckData& _aug) : aug(_aug), n(_n) { - } - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override { - return n > 0 ? ahmn_fork : n; - } -}; - -struct HashmapAugE final : TLB_Complex { - enum { ahme_empty = 0, ahme_root = 1 }; - HashmapAug root_type; - HashmapAugE(int _n, const AugmentationCheckData& _aug) : root_type(_n, std::move(_aug)) { - } - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - bool extract_extra(vm::CellSlice& cs) const; - int get_tag(const vm::CellSlice& cs) const override { - return (int)cs.prefetch_ulong(1); - } -}; - -struct Grams final : TLB_Complex { - bool validate_skip(vm::CellSlice& cs) const override; - td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; - bool null_value(vm::CellBuilder& cb) const override; - bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override; - unsigned precompute_size(const td::BigInt256& value) const; - unsigned precompute_size(td::RefInt256 value) const; -}; - -extern const Grams t_Grams; - -struct MsgAddressInt final : TLB_Complex { - enum { addr_std = 2, addr_var = 3 }; - bool validate_skip(vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override { - return (int)cs.prefetch_ulong(2); - } - ton::AccountIdPrefixFull get_prefix(vm::CellSlice&& cs) const; - ton::AccountIdPrefixFull get_prefix(const vm::CellSlice& cs) const; - ton::AccountIdPrefixFull get_prefix(Ref cs_ref) const; - bool skip_get_depth(vm::CellSlice& cs, int& depth) const; - bool extract_std_address(Ref cs_ref, ton::WorkchainId& workchain, ton::StdSmcAddress& addr, - bool rewrite = true) const; - bool extract_std_address(vm::CellSlice& cs, ton::WorkchainId& workchain, ton::StdSmcAddress& addr, - bool rewrite = true) const; -}; - -extern const MsgAddressInt t_MsgAddressInt; - -struct MsgAddressExt final : TLB { - enum { addr_none = 0, addr_ext = 1 }; - int get_size(const vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override { - return (int)cs.prefetch_ulong(2); - } -}; - -extern const MsgAddressExt t_MsgAddressExt; - -struct MsgAddress final : TLB_Complex { - enum { addr_none = 0, addr_ext = 1, addr_std = 2, addr_var = 3 }; - bool validate_skip(vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override { - return (int)cs.prefetch_ulong(2); - } -}; - -extern const MsgAddress t_MsgAddress; - -struct ExtraCurrencyCollection final : TLB { - HashmapE dict_type; - ExtraCurrencyCollection() : dict_type(32, t_VarUIntegerPos_32) { - } - int get_size(const vm::CellSlice& cs) const override { - return dict_type.get_size(cs); - } - bool validate(const vm::CellSlice& cs) const override { - return dict_type.validate(cs); - } - bool null_value(vm::CellBuilder& cb) const override { - return cb.store_zeroes_bool(1); - } - bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override { - return dict_type.add_values(cb, cs1, cs2); - } - int sub_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override { - return dict_type.sub_values(cb, cs1, cs2); - } - bool add_values_ref(Ref& res, Ref arg1, Ref arg2) const { - return dict_type.add_values_ref(res, std::move(arg1), std::move(arg2)); - } - int sub_values_ref(Ref& res, Ref arg1, Ref arg2) const { - return dict_type.sub_values_ref(res, std::move(arg1), std::move(arg2)); - } - bool store_ref(vm::CellBuilder& cb, Ref arg) const { - return dict_type.store_ref(cb, std::move(arg)); - } - unsigned precompute_size(Ref arg) const { - return arg.is_null() ? 1 : 0x10001; - } -}; - -extern const ExtraCurrencyCollection t_ExtraCurrencyCollection; - -struct CurrencyCollection final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; - bool null_value(vm::CellBuilder& cb) const override { - return cb.store_bits_same_bool(1 + 4, false); - } - bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override; - bool unpack_special(vm::CellSlice& cs, td::RefInt256& balance, Ref& extra) const; - bool pack_special(vm::CellBuilder& cb, td::RefInt256 balance, Ref extra) const; - unsigned precompute_size(td::RefInt256 balance, Ref extra) const { - return t_Grams.precompute_size(std::move(balance)) + t_ExtraCurrencyCollection.precompute_size(std::move(extra)); - } -}; - -extern const CurrencyCollection t_CurrencyCollection; - -struct CommonMsgInfo final : TLB_Complex { - enum { int_msg_info = 0, ext_in_msg_info = 2, ext_out_msg_info = 3 }; - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override { - int v = (int)cs.prefetch_ulong(2); - return v == 1 ? int_msg_info : v; - } - struct Record_int_msg_info; - bool unpack(vm::CellSlice& cs, Record_int_msg_info& data) const; - bool get_created_lt(vm::CellSlice& cs, unsigned long long& created_lt) const; - bool is_internal(const vm::CellSlice& cs) const { - return get_tag(cs) == int_msg_info; - } -}; - -struct CommonMsgInfo::Record_int_msg_info { - bool ihr_disabled, bounce, bounced; - Ref src, dest, value, ihr_fee, fwd_fee; - unsigned long long created_lt; - unsigned created_at; -}; - -extern const CommonMsgInfo t_CommonMsgInfo; - -struct TickTock final : TLB { - int get_size(const vm::CellSlice& cs) const override { - return 2; - } -}; - -extern const TickTock t_TickTock; - -struct StateInit final : TLB_Complex { - bool validate_skip(vm::CellSlice& cs) const override; - bool get_ticktock(vm::CellSlice& cs, int& ticktock) const; -}; - -extern const StateInit t_StateInit; - -struct Message final : TLB_Complex { - bool validate_skip(vm::CellSlice& cs) const override; - bool extract_info(vm::CellSlice& cs) const; - bool get_created_lt(vm::CellSlice& cs, unsigned long long& created_lt) const; - bool is_internal(const vm::CellSlice& cs) const { - return t_CommonMsgInfo.is_internal(cs); - } - bool is_internal(Ref ref) const; -}; - -extern const Message t_Message; -extern const RefTo t_Ref_Message; - -struct IntermediateAddress final : TLB_Complex { - enum { interm_addr_regular = 0, interm_addr_simple = 2, interm_addr_ext = 3 }; - int get_size(const vm::CellSlice& cs) const override; - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - bool fetch_regular(vm::CellSlice& cs, int& use_dst_bits) const { - return cs.fetch_uint_to(8, use_dst_bits) && use_dst_bits <= 96; - } - int get_tag(const vm::CellSlice& cs) const override { - int v = (int)cs.prefetch_ulong(2); - return v == 1 ? interm_addr_regular : v; - } -}; - -extern const IntermediateAddress t_IntermediateAddress; - -struct MsgEnvelope final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - bool extract_fwd_fees_remaining(vm::CellSlice& cs) const; - struct Record { - typedef MsgEnvelope type_class; - Ref cur_addr, next_addr, fwd_fee_remaining; - Ref msg; - }; - struct Record_std { - typedef MsgEnvelope type_class; - int cur_addr, next_addr; - td::RefInt256 fwd_fee_remaining; - Ref msg; - }; - bool unpack(vm::CellSlice& cs, Record& data) const; - bool unpack(vm::CellSlice& cs, Record_std& data) const; - bool unpack_std(vm::CellSlice& cs, int& cur_a, int& nhop_a, Ref& msg) const; - bool get_created_lt(const vm::CellSlice& cs, unsigned long long& created_lt) const; -}; - -extern const MsgEnvelope t_MsgEnvelope; -extern const RefTo t_Ref_MsgEnvelope; - -struct StorageUsed final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; -}; - -extern const StorageUsed t_StorageUsed; - -struct StorageUsedShort final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; -}; - -extern const StorageUsedShort t_StorageUsedShort; - -struct StorageInfo final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; -}; - -extern const StorageInfo t_StorageInfo; - -struct AccountState final : TLB_Complex { - enum { account_uninit = 0, account_frozen = 1, account_active = 2 }; - bool validate_skip(vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override { - int t = (int)cs.prefetch_ulong(2); - return t == 3 ? account_active : t; - } - bool get_ticktock(vm::CellSlice& cs, int& ticktock) const; -}; - -extern const AccountState t_AccountState; - -struct AccountStorage final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - bool skip_copy_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const; -}; - -extern const AccountStorage t_AccountStorage; - -struct Account final : TLB_Complex { - enum { account_none = 0, account = 1 }; - bool allow_empty; - Account(bool _allow_empty = false) : allow_empty(_allow_empty) { - } - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - // Ref get_balance(const vm::CellSlice& cs) const; - bool skip_copy_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const; - bool skip_copy_depth_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const; - int get_tag(const vm::CellSlice& cs) const override { - return (int)cs.prefetch_ulong(1); - } -}; - -extern const Account t_Account, t_AccountE; -extern const RefTo t_Ref_Account; - -struct AccountStatus final : TLB { - enum { acc_state_uninit, acc_state_frozen, acc_state_active, acc_state_nonexist }; - int get_size(const vm::CellSlice& cs) const override { - return 2; - } - int get_tag(const vm::CellSlice& cs) const override { - return (int)cs.prefetch_ulong(2); - } -}; - -extern const AccountStatus t_AccountStatus; - -struct ShardAccount final : TLB_Complex { - int get_size(const vm::CellSlice& cs) const override { - return 0x10140; - } - bool skip(vm::CellSlice& cs) const override { - return cs.advance_ext(0x140, 1); - } - bool validate_skip(vm::CellSlice& cs) const override { - return cs.advance(0x140) && t_Ref_Account.validate_skip(cs); - } -}; - -extern const ShardAccount t_ShardAccount; - -struct DepthBalanceInfo final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - bool null_value(vm::CellBuilder& cb) const override; - bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override; -}; - -extern const DepthBalanceInfo t_DepthBalanceInfo; - -struct Aug_ShardAccounts final : AugmentationCheckData { - Aug_ShardAccounts() : AugmentationCheckData(t_ShardAccount, t_DepthBalanceInfo) { - } - bool eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const override; -}; - -extern const Aug_ShardAccounts aug_ShardAccounts; - -struct ShardAccounts final : TLB_Complex { - HashmapAugE dict_type; - ShardAccounts() : dict_type(256, aug_ShardAccounts){}; - bool skip(vm::CellSlice& cs) const override { - return dict_type.skip(cs); - } - bool validate_skip(vm::CellSlice& cs) const override { - return dict_type.validate_skip(cs); - } -}; - -extern const ShardAccounts t_ShardAccounts; - -struct AccStatusChange final : TLB { - enum { acst_unchanged = 0, acst_frozen = 2, acst_deleted = 3 }; - int get_size(const vm::CellSlice& cs) const override { - return cs.prefetch_ulong(1) ? 2 : 1; - } - int get_tag(const vm::CellSlice& cs) const override { - if (cs.size() == 1) { - return (int)cs.prefetch_ulong(1) ? -1 : acst_unchanged; - } - int v = (int)cs.prefetch_ulong(2); - return v == 1 ? acst_unchanged : v; - } -}; - -extern const AccStatusChange t_AccStatusChange; - -struct TrStoragePhase final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; -}; - -extern const TrStoragePhase t_TrStoragePhase; - -struct TrCreditPhase final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; -}; - -extern const TrCreditPhase t_TrCreditPhase; - -struct TrComputeInternal1 final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; -}; - -struct ComputeSkipReason final : TLB { - enum { cskip_no_state = 0, cskip_bad_state = 1, cskip_no_gas = 2 }; - int get_size(const vm::CellSlice& cs) const override { - return 2; - } - bool validate_skip(vm::CellSlice& cs) const override { - return get_tag(cs) >= 0 && cs.advance(2); - } - int get_tag(const vm::CellSlice& cs) const override { - int t = (int)cs.prefetch_ulong(2); - return t < 3 ? t : -1; - } -}; - -extern const ComputeSkipReason t_ComputeSkipReason; - -struct TrComputePhase final : TLB_Complex { - enum { tr_phase_compute_skipped = 0, tr_phase_compute_vm = 1 }; - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override { - return (int)cs.prefetch_ulong(1); - } -}; - -extern const TrComputePhase t_TrComputePhase; - -struct TrActionPhase final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; -}; - -extern const TrActionPhase t_TrActionPhase; - -struct TrBouncePhase final : TLB_Complex { - enum { tr_phase_bounce_negfunds = 0, tr_phase_bounce_nofunds = 1, tr_phase_bounce_ok = 2 }; - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override; -}; - -extern const TrBouncePhase t_TrBouncePhase; - -struct SplitMergeInfo final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; -}; - -extern const SplitMergeInfo t_SplitMergeInfo; - -struct TransactionDescr final : TLB_Complex { - enum { - trans_ord = 0, - trans_storage = 1, - trans_tick_tock = 2, - trans_split_prepare = 4, - trans_split_install = 5, - trans_merge_prepare = 6, - trans_merge_install = 7 - }; - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override; -}; - -extern const TransactionDescr t_TransactionDescr; - -struct Transaction final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - bool get_total_fees(vm::CellSlice&& cs, td::RefInt256& total_fees) const; -}; - -extern const Transaction t_Transaction; -extern const RefTo t_Ref_Transaction; - -struct Aug_AccountTransactions final : AugmentationCheckData { - Aug_AccountTransactions() : AugmentationCheckData(t_Ref_Transaction, t_Grams) { - } - bool eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const override; -}; - -extern const Aug_AccountTransactions aug_AccountTransactions; -extern const HashmapAug t_AccountTransactions; // (HashmapAug 64 ^Transaction Grams) - -struct HashUpdate final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override { - return cs.advance(8 + 256 * 2); - } - bool validate_skip(vm::CellSlice& cs) const override { - return cs.fetch_ulong(8) == 0x72 && cs.advance(256 * 2); - } -}; - -extern const HashUpdate t_HashUpdate; -extern const RefTo t_Ref_HashUpdate; - -struct AccountBlock final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - bool get_total_fees(vm::CellSlice&& cs, td::RefInt256& total_fees) const; -}; - -extern const AccountBlock t_AccountBlock; - -struct Aug_ShardAccountBlocks final : AugmentationCheckData { - Aug_ShardAccountBlocks() : AugmentationCheckData(t_AccountBlock, t_Grams) { - } - bool eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const override; -}; - -extern const Aug_ShardAccountBlocks aug_ShardAccountBlocks; -extern const HashmapAugE t_ShardAccountBlocks; // (HashmapAugE 256 AccountBlock Grams) - -struct ImportFees final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - bool null_value(vm::CellBuilder& cb) const override { - return cb.store_bits_same_bool(4 + 4 + 1, false); - } - bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override; -}; - -extern const ImportFees t_ImportFees; - -struct InMsg final : TLB_Complex { - enum { - msg_import_ext = 0, - msg_import_ihr = 2, - msg_import_imm = 3, - msg_import_fin = 4, - msg_import_tr = 5, - msg_discard_fin = 6, - msg_discard_tr = 7 - }; - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override { - return (int)cs.prefetch_ulong(3); - } - bool get_import_fees(vm::CellBuilder& cb, vm::CellSlice& cs) const; -}; - -extern const InMsg t_InMsg; - -struct OutMsg final : TLB_Complex { - enum { - msg_export_ext = 0, - msg_export_new = 1, - msg_export_imm = 2, - msg_export_tr = 3, - msg_export_deq_imm = 4, - msg_export_deq = 6, - msg_export_tr_req = 7 - }; - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override { - return (int)cs.prefetch_ulong(3); - } - bool get_export_value(vm::CellBuilder& cb, vm::CellSlice& cs) const; - bool get_created_lt(vm::CellSlice& cs, unsigned long long& created_lt) const; -}; - -extern const OutMsg t_OutMsg; - -// next: InMsgDescr, OutMsgDescr, OutMsgQueue, and their augmentations - -struct Aug_InMsgDescr final : AugmentationCheckData { - Aug_InMsgDescr() : AugmentationCheckData(t_InMsg, t_ImportFees) { - } - bool eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const override { - return t_InMsg.get_import_fees(cb, cs); - } -}; - -extern const Aug_InMsgDescr aug_InMsgDescr; - -struct InMsgDescr final : TLB_Complex { - HashmapAugE dict_type; - InMsgDescr() : dict_type(256, aug_InMsgDescr){}; - bool skip(vm::CellSlice& cs) const override { - return dict_type.skip(cs); - } - bool validate_skip(vm::CellSlice& cs) const override { - return dict_type.validate_skip(cs); - } -}; - -extern const InMsgDescr t_InMsgDescr; - -struct Aug_OutMsgDescr final : AugmentationCheckData { - Aug_OutMsgDescr() : AugmentationCheckData(t_OutMsg, t_CurrencyCollection) { - } - bool eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const override { - return t_OutMsg.get_export_value(cb, cs); - } -}; - -extern const Aug_OutMsgDescr aug_OutMsgDescr; - -struct OutMsgDescr final : TLB_Complex { - HashmapAugE dict_type; - OutMsgDescr() : dict_type(256, aug_OutMsgDescr){}; - bool skip(vm::CellSlice& cs) const override { - return dict_type.skip(cs); - } - bool validate_skip(vm::CellSlice& cs) const override { - return dict_type.validate_skip(cs); - } -}; - -extern const OutMsgDescr t_OutMsgDescr; - -struct EnqueuedMsg final : TLB_Complex { - int get_size(const vm::CellSlice& cs) const override { - return 0x10040; - } - bool skip(vm::CellSlice& cs) const override { - return cs.advance_ext(0x10040); - } - bool validate_skip(vm::CellSlice& cs) const override; - bool unpack(vm::CellSlice& cs, EnqueuedMsgDescr& descr) const { - return descr.unpack(cs); - } -}; - -extern const EnqueuedMsg t_EnqueuedMsg; - -struct Aug_OutMsgQueue final : AugmentationCheckData { - Aug_OutMsgQueue() : AugmentationCheckData(t_EnqueuedMsg, t_uint64) { - } - bool eval_fork(vm::CellBuilder& cb, vm::CellSlice& left_cs, vm::CellSlice& right_cs) const override; - bool eval_empty(vm::CellBuilder& cb) const override; - bool eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const override; -}; - -extern const Aug_OutMsgQueue aug_OutMsgQueue; +struct ShardState { + enum { verbosity = 0 }; + ton::BlockIdExt id_; + Ref root_; + int global_id_; + ton::UnixTime utime_; + ton::LogicalTime lt_; + ton::BlockSeqno mc_blk_seqno_; + bool before_split_{false}; + std::unique_ptr account_dict_; + std::unique_ptr shard_libraries_; + Ref mc_state_extra_; + td::uint64 overload_history_{0}, underload_history_{0}; + td::RefInt256 total_balance_, total_validator_fees_; + Ref total_balance_extra_; + std::unique_ptr out_msg_queue_; + std::unique_ptr ihr_pending_; + std::shared_ptr processed_upto_; -struct OutMsgQueue final : TLB_Complex { - HashmapAugE dict_type; - OutMsgQueue() : dict_type(32 + 64 + 256, aug_OutMsgQueue){}; - bool skip(vm::CellSlice& cs) const override { - return dict_type.skip(cs); + bool is_valid() const { + return id_.is_valid(); } - bool validate_skip(vm::CellSlice& cs) const override { - return dict_type.validate_skip(cs); + bool invalidate() { + id_.invalidate(); + return false; } -}; - -extern const OutMsgQueue t_OutMsgQueue; - -struct ProcessedUpto final : TLB { - int get_size(const vm::CellSlice& cs) const override { - return 64 + 256; + td::Status unpack_state(ton::BlockIdExt id, Ref state_root); + td::Status unpack_state_ext(ton::BlockIdExt id, Ref state_root, int global_id, + ton::BlockSeqno prev_mc_block_seqno, bool after_split, bool after_merge, + std::function for_each_mcseqno); + td::Status merge_with(ShardState& sib); + td::Result> compute_split_out_msg_queue(ton::ShardIdFull subshard); + td::Result> compute_split_processed_upto( + ton::ShardIdFull subshard); + td::Status split(ton::ShardIdFull subshard); + td::Status unpack_out_msg_queue_info(Ref out_msg_queue_info); + bool clear_load_history() { + overload_history_ = underload_history_ = 0; + return true; } -}; - -extern const ProcessedUpto t_ProcessedUpto; -extern const HashmapE t_ProcessedInfo; -extern const HashmapE t_IhrPendingInfo; - -struct OutMsgQueueInfo final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; -}; - -extern const OutMsgQueueInfo t_OutMsgQueueInfo; -extern const RefTo t_Ref_OutMsgQueueInfo; - -struct ExtBlkRef final : TLB { - enum { fixed_size = 64 + 32 + 256 * 2 }; - int get_size(const vm::CellSlice& cs) const override { - return fixed_size; + bool clear_load_history_if(bool cond) { + return cond || clear_load_history(); } -}; - -extern const ExtBlkRef t_ExtBlkRef; + td::Status check_before_split(bool before_split) const; + td::Status check_global_id(int req_global_id) const; + td::Status check_mc_blk_seqno(ton::BlockSeqno last_mc_block_seqno) const; + bool update_prev_utime_lt(ton::UnixTime& prev_utime, ton::LogicalTime& prev_lt) const; -struct BlkMasterInfo final : TLB { - int get_size(const vm::CellSlice& cs) const override { - return t_ExtBlkRef.get_size(cs); + bool for_each_mcseqno(std::function func) const { + return processed_upto_ && processed_upto_->for_each_mcseqno(std::move(func)); } }; -extern const BlkMasterInfo t_BlkMasterInfo; +namespace tlb { +struct CurrencyCollection; +} // namespace tlb -struct ShardIdent final : TLB_Complex { - struct Record; - int get_size(const vm::CellSlice& cs) const override { - return 2 + 6 + 32 + 64; - } - bool skip(vm::CellSlice& cs) const override { - return cs.advance(get_size(cs)); +struct CurrencyCollection { + using type_class = block::tlb::CurrencyCollection; + td::RefInt256 grams; + Ref extra; + CurrencyCollection() = default; + explicit CurrencyCollection(td::RefInt256 _grams, Ref _extra = {}) + : grams(std::move(_grams)), extra(std::move(_extra)) { } - bool validate_skip(vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override { - return 0; + explicit CurrencyCollection(long long _grams, Ref _extra = {}) + : grams(true, _grams), extra(std::move(_extra)) { } - bool unpack(vm::CellSlice& cs, Record& data) const; - bool pack(vm::CellBuilder& cb, const Record& data) const; - bool unpack(vm::CellSlice& cs, ton::ShardIdFull& data) const; - bool pack(vm::CellBuilder& cb, ton::ShardIdFull data) const; - bool unpack(vm::CellSlice& cs, ton::WorkchainId& workchain, ton::ShardId& shard) const; - bool pack(vm::CellBuilder& cb, ton::WorkchainId workchain, ton::ShardId shard) const; -}; - -struct ShardIdent::Record { - int shard_pfx_bits; - int workchain_id; - unsigned long long shard_prefix; - Record() : shard_pfx_bits(-1) { + bool set_zero() { + grams = td::RefInt256{true, 0}; + extra.clear(); + return true; } - Record(int _pfxlen, int _wcid, unsigned long long _pfx) - : shard_pfx_bits(_pfxlen), workchain_id(_wcid), shard_prefix(_pfx) { + static CurrencyCollection zero() { + return CurrencyCollection(td::RefInt256{true, 0}); } - bool check() const; bool is_valid() const { - return shard_pfx_bits >= 0; - } - void invalidate() { - shard_pfx_bits = -1; + return grams.not_null(); } -}; - -extern const ShardIdent t_ShardIdent; - -struct BlockIdExt final : TLB_Complex { - int get_size(const vm::CellSlice& cs) const override { - return 2 + 6 + 32 + 64 + 32 + 256 * 2; + bool is_zero() const { + return is_valid() && extra.is_null() && !td::sgn(grams); } - bool skip(vm::CellSlice& cs) const override { - return cs.advance(get_size(cs)); + bool has_extra() const { + return extra.not_null(); } - bool validate_skip(vm::CellSlice& cs) const override; - bool unpack(vm::CellSlice& cs, ton::BlockIdExt& data) const; - bool pack(vm::CellBuilder& cb, const ton::BlockIdExt& data) const; -}; - -extern const BlockIdExt t_BlockIdExt; - -struct ShardState final : TLB_Complex { - enum { shard_state = (int)0x9023afe1, split_state = 0x5f327da5 }; - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override { - return (int)cs.prefetch_ulong(32) == shard_state ? shard_state : -1; - } -}; - -extern const ShardState t_ShardState; - -struct ShardState_aux final : TLB_Complex { - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override { - return 0; - } -}; - -extern const ShardState_aux t_ShardState_aux; - -struct LibDescr final : TLB_Complex { - enum { shared_lib_descr = 0 }; - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; - int get_tag(const vm::CellSlice& cs) const override { - return (int)cs.prefetch_ulong(2); - } -}; - -extern const LibDescr t_LibDescr; - -struct BlkPrevInfo final : TLB_Complex { - bool merged; - BlkPrevInfo(bool _merged) : merged(_merged) { - } - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; -}; - -extern const BlkPrevInfo t_BlkPrevInfo_0; - -struct McStateExtra final : TLB_Complex { - enum { masterchain_state_extra = 0xcc21 }; - bool skip(vm::CellSlice& cs) const override; - bool validate_skip(vm::CellSlice& cs) const override; -}; - -extern const McStateExtra t_McStateExtra; - -struct KeyExtBlkRef final : TLB { - enum { fixed_size = 1 + ExtBlkRef::fixed_size }; - int get_size(const vm::CellSlice& cs) const override { - return fixed_size; + bool invalidate() { + extra.clear(); + grams.clear(); + return false; } + bool validate() const; + bool validate_extra() const; + bool operator==(const CurrencyCollection& other) const; + bool operator!=(const CurrencyCollection& other) const { + return !operator==(other); + } + bool operator==(td::RefInt256 other_grams) const { + return is_valid() && !has_extra() && !td::cmp(grams, other_grams); + } + bool operator!=(td::RefInt256 other_grams) const { + return !operator==(std::move(other_grams)); + } + bool operator>=(const CurrencyCollection& other) const; + bool operator<=(const CurrencyCollection& other) const { + return other >= *this; + } + static bool add(const CurrencyCollection& a, const CurrencyCollection& b, CurrencyCollection& c); + static bool add(const CurrencyCollection& a, CurrencyCollection&& b, CurrencyCollection& c); + CurrencyCollection& operator+=(const CurrencyCollection& other); + CurrencyCollection& operator+=(CurrencyCollection&& other); + CurrencyCollection& operator+=(td::RefInt256 other_grams); + CurrencyCollection operator+(const CurrencyCollection& other) const; + CurrencyCollection operator+(CurrencyCollection&& other) const; + CurrencyCollection operator+(td::RefInt256 other_grams); + static bool sub(const CurrencyCollection& a, const CurrencyCollection& b, CurrencyCollection& c); + static bool sub(const CurrencyCollection& a, CurrencyCollection&& b, CurrencyCollection& c); + CurrencyCollection& operator-=(const CurrencyCollection& other); + CurrencyCollection& operator-=(CurrencyCollection&& other); + CurrencyCollection operator-(const CurrencyCollection& other) const; + CurrencyCollection operator-(CurrencyCollection&& other) const; + bool store(vm::CellBuilder& cb) const; + bool fetch(vm::CellSlice& cs); + bool unpack(Ref csr); + bool validate_unpack(Ref csr); + bool show(std::ostream& os) const; + std::string to_str() const; }; -extern const KeyExtBlkRef t_KeyExtBlkRef; +std::ostream& operator<<(std::ostream& os, const CurrencyCollection& cc); -struct KeyMaxLt final : TLB { - enum { fixed_size = 1 + 64 }; - int get_size(const vm::CellSlice& cs) const override { - return fixed_size; +struct ValueFlow { + CurrencyCollection from_prev_blk, to_next_blk, imported, exported, fees_collected, fees_imported, created, minted; + bool is_valid() const { + return from_prev_blk.is_valid() && minted.is_valid(); } - bool null_value(vm::CellBuilder& cb) const override { - return cb.store_bits_same_bool(fixed_size, false); + bool validate() const; + bool invalidate() { + return from_prev_blk.invalidate(); } - bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override; -}; - -extern const KeyMaxLt t_KeyMaxLt; + bool store(vm::CellBuilder& cb) const; + bool fetch(vm::CellSlice& cs); + bool unpack(Ref csr); + bool show(std::ostream& os) const; + std::string to_str() const; -struct Aug_OldMcBlocksInfo final : AugmentationCheckData { - Aug_OldMcBlocksInfo() : AugmentationCheckData(t_KeyExtBlkRef, t_KeyMaxLt) { - } - bool eval_leaf(vm::CellBuilder& cb, vm::CellSlice& cs) const override; + private: + bool show_one(std::ostream& os, const char* str, const CurrencyCollection& cc) const; }; -extern const Aug_OldMcBlocksInfo aug_OldMcBlocksInfo; +std::ostream& operator<<(std::ostream& os, const ValueFlow& vflow); -} // namespace tlb +int filter_out_msg_queue(vm::AugmentedDictionary& out_queue, ton::ShardIdFull old_shard, ton::ShardIdFull subshard); std::ostream& operator<<(std::ostream& os, const ShardId& shard_id); @@ -1261,12 +435,16 @@ bool valid_config_data(Ref cell, const td::BitArray<256>& addr, bool c bool add_extra_currency(Ref extra1, Ref extra2, Ref& res); bool sub_extra_currency(Ref extra1, Ref extra2, Ref& res); -ton::AccountIdPrefixFull interpolate_addr(ton::AccountIdPrefixFull src, ton::AccountIdPrefixFull dest, +ton::AccountIdPrefixFull interpolate_addr(const ton::AccountIdPrefixFull& src, const ton::AccountIdPrefixFull& dest, int used_dest_bits); +bool interpolate_addr_to(const ton::AccountIdPrefixFull& src, const ton::AccountIdPrefixFull& dest, int used_dest_bits, + ton::AccountIdPrefixFull& res); // result: (transit_addr_dest_bits, nh_addr_dest_bits) std::pair perform_hypercube_routing(ton::AccountIdPrefixFull src, ton::AccountIdPrefixFull dest, ton::ShardIdFull cur, int used_dest_bits = 0); +bool compute_out_msg_queue_key(Ref msg_env, td::BitArray<352>& key); + bool unpack_block_prev_blk(Ref block_root, const ton::BlockIdExt& id, std::vector& prev, ton::BlockIdExt& mc_blkid, bool& after_split, ton::BlockIdExt* fetch_blkid = nullptr); td::Status unpack_block_prev_blk_ext(Ref block_root, const ton::BlockIdExt& id, @@ -1282,7 +460,7 @@ bool get_old_mc_block_id(vm::AugmentedDictionary* prev_blocks_dict, ton::BlockSe bool get_old_mc_block_id(vm::AugmentedDictionary& prev_blocks_dict, ton::BlockSeqno seqno, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr); bool unpack_old_mc_block_id(Ref old_blk_info, ton::BlockSeqno seqno, ton::BlockIdExt& blkid, - ton::LogicalTime* end_lt); + ton::LogicalTime* end_lt = nullptr); bool check_old_mc_block_id(vm::AugmentedDictionary* prev_blocks_dict, const ton::BlockIdExt& blkid); bool check_old_mc_block_id(vm::AugmentedDictionary& prev_blocks_dict, const ton::BlockIdExt& blkid); @@ -1291,4 +469,10 @@ td::Result> get_block_transaction(Ref block_root, ton::W td::Result> get_block_transaction_try(Ref block_root, ton::WorkchainId workchain, const ton::StdSmcAddress& addr, ton::LogicalTime lt); +bool get_transaction_in_msg(Ref trans_ref, Ref& in_msg); +bool is_transaction_in_msg(Ref trans_ref, Ref msg); +bool is_transaction_out_msg(Ref trans_ref, Ref msg); +bool get_transaction_id(Ref trans_ref, ton::StdSmcAddress& account_addr, ton::LogicalTime& lt); +bool get_transaction_owner(Ref trans_ref, ton::StdSmcAddress& addr); + } // namespace block diff --git a/ton-test-liteclient-full/lite-client/crypto/block/block.tlb b/ton-test-liteclient-full/lite-client/crypto/block/block.tlb index a6bd60d..b14f4cf 100644 --- a/ton-test-liteclient-full/lite-client/crypto/block/block.tlb +++ b/ton-test-liteclient-full/lite-client/crypto/block/block.tlb @@ -250,9 +250,8 @@ account_descr$_ account:^Account last_trans_hash:bits256 depth_balance$_ split_depth:(#<= 30) balance:CurrencyCollection = DepthBalanceInfo; -// _ (HashmapAugE 256 ShardAccount CurrencyCollection) = ShardAccounts; _ (HashmapAugE 256 ShardAccount DepthBalanceInfo) = ShardAccounts; -// + transaction$0110 account_addr:bits256 lt:uint64 prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32 outmsg_cnt:uint15 @@ -530,6 +529,7 @@ masterchain_block_extra#cca1 // validator#53 public_key:SigPubKey weight:uint64 = ValidatorDescr; +validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = ValidatorDescr; validators#11 utime_since:uint32 utime_until:uint32 total:(## 16) main:(## 16) { main <= total } { main >= 1 } list:(Hashmap 16 ValidatorDescr) = ValidatorSet; @@ -600,8 +600,11 @@ _ CatchainConfig = ConfigParam 28; _ fundamental_smc_addr:(HashmapE 256 True) = ConfigParam 31; _ prev_validators:ValidatorSet = ConfigParam 32; +_ prev_temp_validators:ValidatorSet = ConfigParam 33; _ cur_validators:ValidatorSet = ConfigParam 34; +_ cur_temp_validators:ValidatorSet = ConfigParam 35; _ next_validators:ValidatorSet = ConfigParam 36; +_ next_temp_validators:ValidatorSet = ConfigParam 37; validator_temp_key#3 adnl_addr:bits256 temp_public_key:SigPubKey seqno:# valid_until:uint32 = ValidatorTempKey; signed_temp_key#4 key:^ValidatorTempKey signature:CryptoSignature = ValidatorSignedTempKey; diff --git a/ton-test-liteclient-full/lite-client/crypto/block/check-proof.cpp b/ton-test-liteclient-full/lite-client/crypto/block/check-proof.cpp index 2e8040f..fea9fad 100644 --- a/ton-test-liteclient-full/lite-client/crypto/block/check-proof.cpp +++ b/ton-test-liteclient-full/lite-client/crypto/block/check-proof.cpp @@ -1,5 +1,6 @@ #include "check-proof.h" #include "block/block.h" +#include "block/block-parse.h" #include "block/block-auto.h" #include "block/mc-config.h" diff --git a/ton-test-liteclient-full/lite-client/crypto/block/create-state.cpp b/ton-test-liteclient-full/lite-client/crypto/block/create-state.cpp index 3345825..a027b9d 100644 --- a/ton-test-liteclient-full/lite-client/crypto/block/create-state.cpp +++ b/ton-test-liteclient-full/lite-client/crypto/block/create-state.cpp @@ -29,6 +29,7 @@ #include "td/utils/port/signals.h" #include "block.h" +#include "block-parse.h" #include "block-auto.h" #include "mc-config.h" diff --git a/ton-test-liteclient-full/lite-client/crypto/block/mc-config.cpp b/ton-test-liteclient-full/lite-client/crypto/block/mc-config.cpp index eba4370..498ae8d 100644 --- a/ton-test-liteclient-full/lite-client/crypto/block/mc-config.cpp +++ b/ton-test-liteclient-full/lite-client/crypto/block/mc-config.cpp @@ -1,5 +1,6 @@ #include "mc-config.h" #include "block/block.h" +#include "block/block-parse.h" #include "block/block-auto.h" #include "common/bitstring.h" #include "vm/dict.h" @@ -101,7 +102,7 @@ td::Status ConfigInfo::unpack() { accounts_dict = std::make_unique(accounts_root, 256, block::tlb::aug_ShardAccounts); LOG(DEBUG) << "accounts dictionary created"; } - state_extra_root = root_info.custom->prefetch_ref(); + state_extra_root_ = root_info.custom->prefetch_ref(); if (!is_masterchain()) { if (mode & (needShardHashes | needValidatorSet | needSpecialSmc | needPrevBlocks | needWorkchainInfo)) { return td::Status::Error("cannot extract masterchain-specific configuration data from a non-masterchain state"); @@ -110,9 +111,9 @@ td::Status ConfigInfo::unpack() { return td::Status::OK(); } gen::McStateExtra::Record extra_info; - if (!tlb::unpack_cell(state_extra_root, extra_info)) { - vm::load_cell_slice(state_extra_root).print_rec(std::cerr); - block::gen::t_McStateExtra.print_ref(std::cerr, state_extra_root); + if (!tlb::unpack_cell(state_extra_root_, extra_info)) { + vm::load_cell_slice(state_extra_root_).print_rec(std::cerr); + block::gen::t_McStateExtra.print_ref(std::cerr, state_extra_root_); return td::Status::Error("state extra information is invalid"); } gen::ValidatorInfo::Record validator_info; @@ -198,11 +199,11 @@ td::Status Config::unpack() { } config_dict = std::make_unique(config_root, 32); if (mode & needValidatorSet) { - auto vset_res = unpack_validator_set(get_config_param(34)); + auto vset_res = unpack_validator_set(get_config_param(35, 34)); if (vset_res.is_error()) { return vset_res.move_as_error(); } - cur_validators = vset_res.move_as_ok(); + cur_validators_ = vset_res.move_as_ok(); } if (mode & needSpecialSmc) { LOG(DEBUG) << "needSpecialSmc flag set"; @@ -228,10 +229,12 @@ td::Status Config::visit_validator_params() const { // current validator set TRY_RESULT(vset, unpack_validator_set(get_config_param(34))); } - auto vs = get_config_param(36); - if (vs.not_null()) { - // next validator set - TRY_RESULT(vset, unpack_validator_set(std::move(vs))); + for (int i = 32; i < 38; i++) { + // prev/current/next persistent and temporary validator sets + auto vs = get_config_param(i); + if (vs.not_null()) { + TRY_RESULT(vset, unpack_validator_set(std::move(vs))); + } } get_catchain_validators_config(); return td::Status::OK(); @@ -319,9 +322,14 @@ td::Result> Config::unpack_validator_set(Refempty_ext())) { + return td::Status::Error(PSLICE() << "validator #" << i + << " has an invalid ValidatorDescr record in the validator set dictionary"); + } } gen::SigPubKey::Record sig_pubkey; if (!tlb::csr_unpack(std::move(descr.public_key), sig_pubkey)) { @@ -334,7 +342,7 @@ td::Result> Config::unpack_validator_set(Ref ~(ptr->total_weight)) { return td::Status::Error("total weight of all validators in validator set exceeds 2^64"); } - ptr->list.emplace_back(sig_pubkey.pubkey, descr.weight, ptr->total_weight); + ptr->list.emplace_back(sig_pubkey.pubkey, descr.weight, ptr->total_weight, descr.adnl_addr); ptr->total_weight += descr.weight; } return std::move(ptr); @@ -367,7 +375,7 @@ void ConfigInfo::cleanup() { state_root.clear(); } if (!(mode & needStateExtraRoot)) { - state_extra_root.clear(); + state_extra_root_.clear(); } } @@ -378,6 +386,18 @@ Ref Config::get_config_param(int idx) const { return config_dict->lookup_ref(td::BitArray<32>{idx}); } +Ref Config::get_config_param(int idx, int idx2) const { + if (!config_dict) { + return {}; + } + auto res = config_dict->lookup_ref(td::BitArray<32>{idx}); + if (res.not_null()) { + return res; + } else { + return config_dict->lookup_ref(td::BitArray<32>{idx2}); + } +} + td::Result> Config::get_block_limits(bool is_masterchain) const { int param = (is_masterchain ? 22 : 23); auto cell = get_config_param(param); @@ -429,6 +449,13 @@ CatchainValidatorsConfig Config::get_catchain_validators_config() const { return unpack_catchain_validators_config(get_config_param(28)); } +// compares all fields except fsm*, before_merge_, nx_cc_updated_, next_catchain_seqno_ +bool McShardHash::basic_info_equal(const McShardHash& other) const { + return blk_ == other.blk_ && start_lt_ == other.start_lt_ && end_lt_ == other.end_lt_ && + gen_utime_ == other.gen_utime_ && min_ref_mc_seqno_ == other.min_ref_mc_seqno_ && + before_split_ == other.before_split_ && want_split_ == other.want_split_ && want_merge_ == other.want_merge_; +} + void McShardHash::set_fsm(FsmState fsm, ton::UnixTime fsm_utime, ton::UnixTime fsm_interval) { fsm_ = fsm; fsm_utime_ = fsm_utime; @@ -442,7 +469,7 @@ Ref McShardHash::unpack(vm::CellSlice& cs, ton::ShardIdFull id) { } auto res = Ref(true, ton::BlockId{id, (unsigned)descr.seq_no}, descr.start_lt, descr.end_lt, descr.gen_utime, descr.root_hash, descr.file_hash, descr.min_ref_mc_seqno, - descr.next_catchain_seqno, descr.next_validator_shard, descr.nx_cc_updated, + descr.next_catchain_seqno, descr.next_validator_shard, /* descr.nx_cc_updated */ false, descr.before_split, descr.before_merge, descr.want_split, descr.want_merge); McShardHash& sh = res.unique_write(); switch (gen::t_FutureSplitMerge.get_tag(*(descr.split_merge_at))) { @@ -477,7 +504,7 @@ bool McShardHash::pack(vm::CellBuilder& cb) const { && cb.store_bool_bool(before_merge_) // before_merge:Bool && cb.store_bool_bool(want_split_) // want_split:Bool && cb.store_bool_bool(want_merge_) // want_merge:Bool - && cb.store_bool_bool(nx_cc_updated_) // nx_cc_updated:Bool + && cb.store_bool_bool(false) // nx_cc_updated:Bool && cb.store_long_bool(0, 3) // flags:(## 3) { flags = 0 } && cb.store_long_bool(next_catchain_seqno_, 32) // next_catchain_seqno:uint32 && cb.store_long_bool(next_validator_shard_, 64) // next_validator_shard:uint64 @@ -515,6 +542,23 @@ Ref McShardHash::from_block(Ref block_root, const ton::Fi shard.shard_pfx, false, info.before_split, false, info.want_split, info.want_merge); } +McShardDescr::McShardDescr(const McShardDescr& other) + : McShardHash(other) + , block_root(other.block_root) + , state_root(other.state_root) + , processed_upto(other.processed_upto) { + set_queue_root(other.outmsg_root); +} + +McShardDescr& McShardDescr::operator=(const McShardDescr& other) { + McShardHash::operator=(other); + block_root = other.block_root; + outmsg_root = other.outmsg_root; + processed_upto = other.processed_upto; + set_queue_root(other.outmsg_root); + return *this; +} + Ref McShardDescr::from_block(Ref block_root, Ref state_root, const ton::FileHash& fhash) { if (block_root.is_null()) { @@ -547,6 +591,44 @@ Ref McShardDescr::from_block(Ref block_root, Ref McShardDescr::from_state(ton::BlockIdExt blkid, Ref state_root) { + if (state_root.is_null()) { + return {}; + } + block::gen::ShardStateUnsplit::Record info; + block::gen::OutMsgQueueInfo::Record qinfo; + block::ShardId shard; + if (!(tlb::unpack_cell(state_root, info) && shard.deserialize(info.shard_id.write()) && + tlb::unpack_cell(info.out_msg_queue_info, qinfo))) { + LOG(DEBUG) << "cannot create McShardDescr from a shardchain state"; + return {}; + } + if (ton::ShardIdFull(shard) != ton::ShardIdFull(blkid) || info.seq_no != blkid.seqno()) { + LOG(DEBUG) << "shard id mismatch, cannot construct McShardDescr"; + return {}; + } + auto res = Ref(true, blkid.id, info.gen_lt, info.gen_lt, info.gen_utime, blkid.root_hash, + blkid.file_hash, info.min_ref_mc_seqno, 0, shard.shard_pfx, false, info.before_split); + res.unique_write().state_root = state_root; + res.unique_write().set_queue_root(qinfo.out_queue->prefetch_ref(0)); + return res; +} + +bool McShardDescr::set_queue_root(Ref queue_root) { + outmsg_root = std::move(queue_root); + out_msg_queue = std::make_unique(outmsg_root, 352, block::tlb::aug_OutMsgQueue); + return true; +} + +void McShardDescr::disable() { + block_root.clear(); + state_root.clear(); + outmsg_root.clear(); + out_msg_queue.reset(); + processed_upto.reset(); + McShardHash::disable(); +} + void ConfigInfo::reset_mc_hash() { if (block_id.is_masterchain() && !block_id.root_hash.is_zero()) { // TODO: use block_start_lt instead of lt if available @@ -910,6 +992,22 @@ bool ShardConfig::has_workchain(ton::WorkchainId workchain) const { return shard_hashes_dict_ && shard_hashes_dict_->key_exists(td::BitArray<32>{workchain}); } +std::vector ShardConfig::get_workchains() const { + if (!shard_hashes_dict_) { + return {}; + } + std::vector res; + if (!shard_hashes_dict_->check_for_each([&res](Ref val, td::ConstBitPtr key, int n) { + CHECK(n == 32); + ton::WorkchainId w = (int)key.get_int(32); + res.push_back(w); + return true; + })) { + return {}; + } + return res; +} + bool ShardConfig::new_workchain(ton::WorkchainId workchain, const ton::RootHash& zerostate_root_hash, const ton::FileHash& zerostate_file_hash) { if (!shard_hashes_dict_ || has_workchain(workchain)) { @@ -920,7 +1018,7 @@ bool ShardConfig::new_workchain(ton::WorkchainId workchain, const ton::RootHash& return cb.store_zeroes_bool(1 + 32 + 64 * 2) // bt_leaf$0 ; shard_descr$_ seq_no:uint32 start_lt:uint64 end_lt:uint64 && cb.store_bits_bool(zerostate_root_hash) // root_hash:bits256 && cb.store_bits_bool(zerostate_file_hash) // file_hash:bits256 - && cb.store_long_bool(8, 8) // ... nx_cc_updated:Bool ... + && cb.store_long_bool(0, 8) // ... nx_cc_updated:Bool ... && cb.store_long_bool(0, 32) // next_catchain_seqno:uint32 && cb.store_long_bool(1ULL << 63, 64) // next_validator_shard:uint64 && cb.store_long_bool(~0U, 32) // min_ref_mc_seqno:uint32 @@ -1040,7 +1138,7 @@ td::Result ShardConfig::may_update_shard_block_info(Ref new_i << new_info->next_catchain_seqno_ << " but previous shard configuration expects " << old_cc_seqno + before_merge); } - if (new_info->end_lt_ > lt_limit) { + if (new_info->end_lt_ >= lt_limit) { return td::Status::Error(-666, PSTRING() << "the top shard block update has end_lt " << new_info->end_lt_ << " which is larger than the current limit " << lt_limit); } @@ -1266,14 +1364,24 @@ ton::CatchainSeqno ConfigInfo::get_shard_cc_seqno(ton::ShardIdFull shard) const return shard.is_masterchain() ? cc_seqno_ : ShardConfig::get_shard_cc_seqno(shard); } -std::vector> Config::compute_validator_set( - ton::ShardIdFull shard, const block::ValidatorSet& vset, ton::UnixTime time, ton::CatchainSeqno cc_seqno) const { +std::vector Config::compute_validator_set(ton::ShardIdFull shard, const block::ValidatorSet& vset, + ton::UnixTime time, ton::CatchainSeqno cc_seqno) const { return do_compute_validator_set(get_catchain_validators_config(), shard, vset, time, cc_seqno); } -std::vector> ConfigInfo::compute_validator_set_cc( - ton::ShardIdFull shard, const block::ValidatorSet& vset, ton::UnixTime time, - ton::CatchainSeqno& cc_seqno_delta) const { +std::vector Config::compute_validator_set(ton::ShardIdFull shard, ton::UnixTime time, + ton::CatchainSeqno cc_seqno) const { + if (!cur_validators_) { + return {}; + } else { + return compute_validator_set(shard, *cur_validators_, time, cc_seqno); + } +} + +std::vector ConfigInfo::compute_validator_set_cc(ton::ShardIdFull shard, + const block::ValidatorSet& vset, + ton::UnixTime time, + ton::CatchainSeqno& cc_seqno_delta) const { if (cc_seqno_delta & -2) { return {}; } @@ -1321,11 +1429,21 @@ const ValidatorDescr& ValidatorSet::at_weight(td::uint64 weight_pos) const { return *--it; } -std::vector> Config::do_compute_validator_set( - const block::CatchainValidatorsConfig& ccv_conf, ton::ShardIdFull shard, const block::ValidatorSet& vset, - ton::UnixTime time, ton::CatchainSeqno cc_seqno) { +std::vector ValidatorSet::export_validator_set() const { + std::vector l; + l.reserve(list.size()); + for (const auto& node : list) { + l.emplace_back(node.pubkey, node.weight, node.adnl_addr); + } + return l; +} + +std::vector Config::do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf, + ton::ShardIdFull shard, + const block::ValidatorSet& vset, ton::UnixTime time, + ton::CatchainSeqno cc_seqno) { LOG(DEBUG) << "in Config::do_compute_validator_set() for " << shard.to_str() << " ; cc_seqno=" << cc_seqno; - std::vector> nodes; + std::vector nodes; bool is_mc = shard.is_masterchain(); unsigned count = std::min(is_mc ? vset.main : ccv_conf.shard_val_num, vset.total); CHECK((unsigned)vset.total == vset.list.size()); @@ -1338,7 +1456,8 @@ std::vector> Co // TODO: write a more clever validator list selection algorithm // (if we really need one for the masterchain) for (unsigned i = 0; i < count; i++) { - nodes.emplace_back(vset.list[i].pubkey, vset.list[i].weight); + const auto& v = vset.list[i]; + nodes.emplace_back(v.pubkey, v.weight, v.adnl_addr); } return nodes; } @@ -1359,7 +1478,7 @@ std::vector> Co } auto& entry = vset.at_weight(p); // LOG(DEBUG) << "vset entry #" << i << ": rem_wt=" << total_wt << ", total_wt=" << vset.total_weight << ", op=" << op << ", p=" << p << "; entry.cum_wt=" << entry.cum_weight << ", entry.wt=" << entry.weight << " " << entry.cum_weight / entry.weight; - nodes.emplace_back(entry.pubkey, 1); // NB: shardchain validator lists have all weights = 1 + nodes.emplace_back(entry.pubkey, 1, entry.adnl_addr); // NB: shardchain validator lists have all weights = 1 CHECK(total_wt >= entry.weight); total_wt -= entry.weight; std::pair new_hole{entry.cum_weight, entry.weight}; @@ -1370,6 +1489,14 @@ std::vector> Co return nodes; } +std::vector Config::compute_total_validator_set(int next) const { + auto res = unpack_validator_set(get_config_param(next < 0 ? 32 : (next ? 36 : 34))); + if (res.is_error()) { + return {}; + } + return res.move_as_ok()->export_validator_set(); +} + bool WorkchainInfo::unpack(ton::WorkchainId wc, vm::CellSlice& cs) { workchain = ton::workchainInvalid; if (wc == ton::workchainInvalid) { diff --git a/ton-test-liteclient-full/lite-client/crypto/block/mc-config.h b/ton-test-liteclient-full/lite-client/crypto/block/mc-config.h index be9d059..dbf5240 100644 --- a/ton-test-liteclient-full/lite-client/crypto/block/mc-config.h +++ b/ton-test-liteclient-full/lite-client/crypto/block/mc-config.h @@ -3,8 +3,7 @@ #include "vm/db/StaticBagOfCellsDb.h" #include "vm/dict.h" #include "ton/ton-types.h" -#include "validator/interfaces/config.h" -#include "validator/interfaces/validator-full-id.h" +#include "ton/ton-shard.h" #include "common/bitstring.h" #include "block.h" #include @@ -17,14 +16,21 @@ using td::Ref; struct ValidatorDescr { ton::Ed25519_PublicKey pubkey; + td::Bits256 adnl_addr; td::uint64 weight; td::uint64 cum_weight; ValidatorDescr() = default; - ValidatorDescr(const td::BitArray<256>& _pubkey, td::uint64 _weight, td::uint64 _cum_weight) + ValidatorDescr(const td::Bits256& _pubkey, td::uint64 _weight, td::uint64 _cum_weight) : pubkey(_pubkey), weight(_weight), cum_weight(_cum_weight) { + adnl_addr.set_zero(); + } + ValidatorDescr(const td::Bits256& _pubkey, td::uint64 _weight, td::uint64 _cum_weight, const td::Bits256& _adnl_addr) + : pubkey(_pubkey), adnl_addr(_adnl_addr), weight(_weight), cum_weight(_cum_weight) { + adnl_addr.set_zero(); } ValidatorDescr(const ton::Ed25519_PublicKey& _pubkey, td::uint64 _weight, td::uint64 _cum_weight) : pubkey(_pubkey), weight(_weight), cum_weight(_cum_weight) { + adnl_addr.set_zero(); } bool operator<(td::uint64 wt_pos) const & { return cum_weight < wt_pos; @@ -46,6 +52,7 @@ struct ValidatorSet { return list[i]; } const ValidatorDescr& at_weight(td::uint64 weight_pos) const; + std::vector export_validator_set() const; }; #pragma pack(push, 1) @@ -112,7 +119,20 @@ class ValidatorSetPRNG { } }; -struct McShardHash : public ton::validator::McShardHash { +class McShardHashI : public td::CntObject { + public: + enum class FsmState { fsm_none, fsm_split, fsm_merge }; + virtual ton::BlockIdExt top_block_id() const = 0; + virtual ton::LogicalTime start_lt() const = 0; + virtual ton::LogicalTime end_lt() const = 0; + virtual ton::UnixTime fsm_utime() const = 0; + virtual FsmState fsm_state() const = 0; + virtual ton::ShardIdFull shard() const = 0; + virtual bool before_split() const = 0; + virtual bool before_merge() const = 0; +}; + +struct McShardHash : public McShardHashI { ton::BlockIdExt blk_; ton::LogicalTime start_lt_, end_lt_; ton::UnixTime gen_utime_{0}; @@ -122,7 +142,6 @@ struct McShardHash : public ton::validator::McShardHash { FsmState fsm_{FsmState::fsm_none}; bool disabled_{false}; bool before_split_{false}, before_merge_{false}, want_split_{false}, want_merge_{false}; - bool nx_cc_updated_{false}; ton::CatchainSeqno next_catchain_seqno_{-1U}; ton::ShardId next_validator_shard_{ton::shardIdAll}; McShardHash(const ton::BlockId& id, ton::LogicalTime start_lt, ton::LogicalTime end_lt, ton::UnixTime gen_utime, @@ -138,7 +157,6 @@ struct McShardHash : public ton::validator::McShardHash { , before_merge_(before_merge) , want_split_(want_split) , want_merge_(want_merge) - , nx_cc_updated_(nx_cc_updated) , next_catchain_seqno_(cc_seqno) , next_validator_shard_(val_shard ? val_shard : id.shard) { } @@ -186,6 +204,9 @@ struct McShardHash : public ton::validator::McShardHash { ton::WorkchainId workchain() const { return blk_.id.workchain; } + bool contains(const ton::AccountIdPrefixFull& pfx) const { + return ton::shard_contains(shard(), pfx); + } bool before_split() const override final { return before_split_; } @@ -196,11 +217,14 @@ struct McShardHash : public ton::validator::McShardHash { return disabled_; } void disable() { + blk_.invalidate(); disabled_ = true; } ton::BlockSeqno seqno() const { return blk_.id.seqno; } + // compares all fields except fsm*, before_merge_, nx_cc_updated_, next_catchain_seqno_ + bool basic_info_equal(const McShardHash& other) const; void clear_fsm() { fsm_ = FsmState::fsm_none; } @@ -211,6 +235,10 @@ struct McShardHash : public ton::validator::McShardHash { void set_fsm_merge(ton::UnixTime fsm_utime, ton::UnixTime fsm_interval) { set_fsm(FsmState::fsm_merge, fsm_utime, fsm_interval); } + bool fsm_equal(const McShardHash& other) const { + return fsm_ == other.fsm_ && + (is_fsm_none() || (fsm_utime_ == other.fsm_utime_ && fsm_interval_ == other.fsm_interval_)); + } bool pack(vm::CellBuilder& cb) const; static Ref unpack(vm::CellSlice& cs, ton::ShardIdFull id); static Ref from_block(Ref block_root, const ton::FileHash& _fhash); @@ -223,6 +251,7 @@ struct McShardDescr final : public McShardHash { Ref block_root; Ref state_root; Ref outmsg_root; + std::unique_ptr out_msg_queue; std::shared_ptr processed_upto; McShardDescr(const ton::BlockId& id, ton::LogicalTime start_lt, ton::LogicalTime end_lt, ton::UnixTime gen_utime, const ton::BlockHash& root_hash, const ton::FileHash& file_hash, ton::BlockSeqno min_ref_mc_seqno = -1u, @@ -236,8 +265,14 @@ struct McShardDescr final : public McShardHash { } McShardDescr(const McShardHash& shard_hash) : McShardHash(shard_hash) { } - McShardDescr(const McShardDescr&) = default; + McShardDescr(const McShardDescr& other); + McShardDescr(McShardDescr&& other) = default; + McShardDescr& operator=(const McShardDescr& other); + McShardDescr& operator=(McShardDescr&& other) = default; + bool set_queue_root(Ref queue_root); + void disable(); static Ref from_block(Ref block_root, Ref state_root, const ton::FileHash& _fhash); + static Ref from_state(ton::BlockIdExt blkid, Ref state_root); }; struct StoragePrices { @@ -315,6 +350,7 @@ class ShardConfig { bool unpack(Ref shard_hashes, Ref mc_shard_hash = {}); Ref get_root_csr() const; bool has_workchain(ton::WorkchainId workchain) const; + std::vector get_workchains() const; Ref get_shard_hash(ton::ShardIdFull id, bool exact = true) const; bool contains(ton::BlockIdExt blkid) const; bool get_shard_hash_raw(vm::CellSlice& cs, ton::ShardIdFull id, ton::ShardIdFull& true_id, bool exact = true) const; @@ -371,13 +407,12 @@ class Config { enum { needValidatorSet = 16, needSpecialSmc = 32, needWorkchainInfo = 256 }; int mode{0}; ton::BlockIdExt block_id; - static constexpr ton::LogicalTime lt_align = 1000000, max_lt_growth = 10 * lt_align - 1; private: td::BitArray<256> config_addr; Ref config_root; std::unique_ptr config_dict; - std::unique_ptr cur_validators; + std::unique_ptr cur_validators_; std::unique_ptr workchains_dict_; WorkchainSet workchains_; @@ -385,7 +420,14 @@ class Config { std::unique_ptr special_smc_dict; public: + static constexpr ton::LogicalTime get_lt_align() { + return 1000000; + } + static constexpr ton::LogicalTime get_max_lt_growth() { + return 10 * get_lt_align() - 1; + } Ref get_config_param(int idx) const; + Ref get_config_param(int idx, int idx2) const; Ref operator[](int idx) const { return get_config_param(idx); } @@ -411,11 +453,15 @@ class Config { return workchains_; } Ref get_workchain_info(ton::WorkchainId workchain_id) const; - std::vector> compute_validator_set( - ton::ShardIdFull shard, const block::ValidatorSet& vset, ton::UnixTime time, ton::CatchainSeqno cc_seqno) const; - static std::vector> do_compute_validator_set( - const block::CatchainValidatorsConfig& ccv_conf, ton::ShardIdFull shard, const block::ValidatorSet& vset, - ton::UnixTime time, ton::CatchainSeqno cc_seqno); + std::vector compute_validator_set(ton::ShardIdFull shard, const block::ValidatorSet& vset, + ton::UnixTime time, ton::CatchainSeqno cc_seqno) const; + std::vector compute_validator_set(ton::ShardIdFull shard, ton::UnixTime time, + ton::CatchainSeqno cc_seqno) const; + std::vector compute_total_validator_set(int next) const; + static std::vector do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf, + ton::ShardIdFull shard, + const block::ValidatorSet& vset, ton::UnixTime time, + ton::CatchainSeqno cc_seqno); static td::Result> unpack_config(Ref mc_state_root, const td::Bits256& config_addr = td::Bits256::zero(), @@ -457,7 +503,7 @@ class ConfigInfo : public Config, public ShardConfig { private: Ref state_root; Ref lib_root_; - Ref state_extra_root; + Ref state_extra_root_; Ref accounts_root; ton::ZeroStateIdExt zerostate_id_; ton::BlockIdExt last_key_block_; @@ -489,6 +535,9 @@ class ConfigInfo : public Config, public ShardConfig { bool is_key_state() const { return is_key_state_; } + Ref get_state_extra_root() const { + return state_extra_root_; + } ton::CatchainSeqno get_shard_cc_seqno(ton::ShardIdFull shard) const; bool get_last_key_block(ton::BlockIdExt& blkid, ton::LogicalTime& blklt, bool strict = false) const; bool get_old_mc_block_id(ton::BlockSeqno seqno, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const; @@ -502,9 +551,9 @@ class ConfigInfo : public Config, public ShardConfig { int get_smc_tick_tock(td::ConstBitPtr smc_addr) const; std::unique_ptr create_accounts_dict() const; const vm::AugmentedDictionary& get_accounts_dict() const; - std::vector> compute_validator_set_cc( - ton::ShardIdFull shard, const block::ValidatorSet& vset, ton::UnixTime time, - ton::CatchainSeqno& cc_seqno_delta) const; + std::vector compute_validator_set_cc(ton::ShardIdFull shard, const block::ValidatorSet& vset, + ton::UnixTime time, + ton::CatchainSeqno& cc_seqno_delta) const; static td::Result> extract_config(std::shared_ptr static_boc, int mode = 0); static td::Result> extract_config(Ref mc_state_root, int mode = 0); diff --git a/ton-test-liteclient-full/lite-client/crypto/block/transaction.cpp b/ton-test-liteclient-full/lite-client/crypto/block/transaction.cpp index a7e78be..0227781 100644 --- a/ton-test-liteclient-full/lite-client/crypto/block/transaction.cpp +++ b/ton-test-liteclient-full/lite-client/crypto/block/transaction.cpp @@ -1,5 +1,6 @@ #include "block/transaction.h" #include "block/block.h" +#include "block/block-parse.h" #include "block/block-auto.h" #include "td/utils/bits.h" #include "td/utils/uint128.h" @@ -273,8 +274,8 @@ bool Account::unpack(Ref shard_account, Ref extra, block::gen::t_ShardAccount.print(std::cerr, *shard_account); } block::gen::ShardAccount::Record acc_info; - if (!(block::gen::t_ShardAccount.validate(*shard_account) && block::tlb::t_ShardAccount.validate(*shard_account) && - tlb::unpack_exact(shard_account.write(), acc_info))) { + if (!(block::gen::t_ShardAccount.validate_csr(shard_account) && + block::tlb::t_ShardAccount.validate_csr(shard_account) && tlb::unpack_exact(shard_account.write(), acc_info))) { LOG(ERROR) << "account " << addr.to_hex() << " state is invalid"; return false; } @@ -1223,7 +1224,7 @@ bool Transaction::check_rewrite_dest_addr(Ref& dest_addr, const A // repack as an addr_var CHECK(tlb::csr_pack(dest_addr, std::move(rec))); } - CHECK(block::gen::t_MsgAddressInt.validate(*dest_addr)); + CHECK(block::gen::t_MsgAddressInt.validate_csr(dest_addr)); return true; } @@ -1254,7 +1255,7 @@ int Transaction::try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const A fwd_fee = ihr_fee = td::RefInt256{true, 0}; } else { // int_msg_info$0 constructor - if (!tlb::unpack(cs2, info) || !block::tlb::t_CurrencyCollection.validate(*info.value)) { + if (!tlb::unpack(cs2, info) || !block::tlb::t_CurrencyCollection.validate_csr(info.value)) { return -1; } fwd_fee = block::tlb::t_Grams.as_integer(info.fwd_fee); @@ -1319,7 +1320,7 @@ int Transaction::try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const A // Process outbound internal message // check value, check/compute ihr_fees, fwd_fees // ... - if (!block::tlb::t_CurrencyCollection.validate(*info.value)) { + if (!block::tlb::t_CurrencyCollection.validate_csr(info.value)) { LOG(DEBUG) << "invalid value:CurrencyCollection in proposed outbound message"; } if (info.ihr_disabled) { @@ -1327,8 +1328,10 @@ int Transaction::try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const A ihr_fee = td::RefInt256{true, 0}; } // extract value to be carried by the message - td::RefInt256 req_grams = block::tlb::t_Grams.as_integer(info.value); - Ref req_extra = info.value->prefetch_ref(); + block::CurrencyCollection value_cc; + CHECK(value_cc.unpack(info.value)); + auto req_grams = std::move(value_cc.grams); + auto req_extra = std::move(value_cc.extra); CHECK(req_grams.not_null()); // compute req_grams + fees @@ -1447,7 +1450,7 @@ int Transaction::try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const A LOG(ERROR) << "generated outbound message is not a valid (Message Any) according to hand-written check"; return -1; } - if (!block::gen::t_Message.validate_ref(new_msg)) { + if (!block::gen::t_Message_Any.validate_ref(new_msg)) { LOG(ERROR) << "generated outbound message is not a valid (Message Any) according to automated check"; return -1; } diff --git a/ton-test-liteclient-full/lite-client/crypto/block/transaction.h b/ton-test-liteclient-full/lite-client/crypto/block/transaction.h index 1d0771e..3e82d53 100644 --- a/ton-test-liteclient-full/lite-client/crypto/block/transaction.h +++ b/ton-test-liteclient-full/lite-client/crypto/block/transaction.h @@ -47,10 +47,10 @@ struct NewOutMsg { NewOutMsg(ton::LogicalTime _lt, Ref _msg, Ref _trans) : lt(_lt), msg(std::move(_msg)), trans(std::move(_trans)) { } - bool operator<(const NewOutMsg& other) const & { + bool operator<(const NewOutMsg& other) const& { return lt < other.lt || (lt == other.lt && msg->get_hash() < other.msg->get_hash()); } - bool operator>(const NewOutMsg& other) const & { + bool operator>(const NewOutMsg& other) const& { return lt > other.lt || (lt == other.lt && other.msg->get_hash() < msg->get_hash()); } }; @@ -238,6 +238,9 @@ struct Account { Account(ton::WorkchainId wc, td::ConstBitPtr _addr, int depth) : split_depth_set_(true), split_depth_((unsigned char)depth), workchain(wc), addr(_addr) { } + block::CurrencyCollection get_balance() const { + return block::CurrencyCollection{balance, extra_balance}; + } bool set_address(ton::WorkchainId wc, td::ConstBitPtr new_addr); bool unpack(Ref account, Ref extra, ton::UnixTime now, bool special = false); bool init_new(ton::UnixTime now); @@ -256,7 +259,7 @@ struct Account { bool create_account_block(vm::CellBuilder& cb); // stores an AccountBlock with all transactions protected: - friend class Transaction; + friend struct Transaction; bool set_split_depth(int split_depth); bool check_split_depth(int split_depth) const; bool init_rewrite_addr(int split_depth, td::ConstBitPtr orig_addr_rewrite); diff --git a/ton-test-liteclient-full/lite-client/crypto/common/bigint.hpp b/ton-test-liteclient-full/lite-client/crypto/common/bigint.hpp index 982e66b..2c29778 100644 --- a/ton-test-liteclient-full/lite-client/crypto/common/bigint.hpp +++ b/ton-test-liteclient-full/lite-client/crypto/common/bigint.hpp @@ -421,7 +421,7 @@ class BigIntG { } template - bool operator!=(const BigIntG& y) { + bool operator!=(const BigIntG& y) const { return !(*this == y); } @@ -433,7 +433,7 @@ class BigIntG { return as_any_int().eq_any(y); } - bool operator!=(word_t y) { + bool operator!=(word_t y) const { return !(*this == y); } bool mul_add_short_bool(word_t y, word_t z) { diff --git a/ton-test-liteclient-full/lite-client/crypto/common/refcnt.cpp b/ton-test-liteclient-full/lite-client/crypto/common/refcnt.cpp new file mode 100644 index 0000000..be6cfa7 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/common/refcnt.cpp @@ -0,0 +1,37 @@ +#include "refcnt.hpp" + +#include "td/utils/ScopeGuard.h" + +namespace td { +namespace detail { +struct SafeDeleter { + public: + void retire(const CntObject *ptr) { + if (is_active_) { + to_delete_.push_back(ptr); + return; + } + is_active_ = true; + SCOPE_EXIT { + is_active_ = false; + }; + delete ptr; + while (!to_delete_.empty()) { + auto *ptr = to_delete_.back(); + to_delete_.pop_back(); + delete ptr; + } + } + + private: + std::vector to_delete_; + bool is_active_{false}; +}; + +TD_THREAD_LOCAL SafeDeleter *deleter; +void safe_delete(const CntObject *ptr) { + init_thread_local(deleter); + deleter->retire(ptr); +} +} // namespace detail +} // namespace td diff --git a/ton-test-liteclient-full/lite-client/crypto/common/refcnt.hpp b/ton-test-liteclient-full/lite-client/crypto/common/refcnt.hpp index dff31ba..329d683 100644 --- a/ton-test-liteclient-full/lite-client/crypto/common/refcnt.hpp +++ b/ton-test-liteclient-full/lite-client/crypto/common/refcnt.hpp @@ -38,7 +38,13 @@ class CntObject { } CntObject(const CntObject& other) : CntObject() { } - void operator=(const CntObject& other) { + CntObject(CntObject&& other) : CntObject() { + } + CntObject& operator=(const CntObject& other) { + return *this; + } + CntObject& operator=(CntObject&& other) { + return *this; } virtual ~CntObject() { auto cnt = cnt_.load(std::memory_order_relaxed); @@ -132,6 +138,9 @@ struct RefValue> { struct static_cast_ref {}; +namespace detail { +void safe_delete(const CntObject* ptr); +} template class Ref { T* ptr; @@ -306,7 +315,7 @@ class Ref { template static void release_shared(S* obj, int cnt = 1) { if (obj->dec(cnt)) { - delete obj; + detail::safe_delete(obj); } } template diff --git a/ton-test-liteclient-full/lite-client/crypto/common/refint.cpp b/ton-test-liteclient-full/lite-client/crypto/common/refint.cpp index 37538d7..63ad9dc 100644 --- a/ton-test-liteclient-full/lite-client/crypto/common/refint.cpp +++ b/ton-test-liteclient-full/lite-client/crypto/common/refint.cpp @@ -195,30 +195,6 @@ int sgn(RefInt256 x) { return x->sgn(); } -bool operator==(RefInt256 x, RefInt256 y) { - return cmp(x, y) == 0; -} - -bool operator!=(RefInt256 x, RefInt256 y) { - return cmp(x, y) != 0; -} - -bool operator<(RefInt256 x, RefInt256 y) { - return cmp(x, y) < 0; -} - -bool operator>(RefInt256 x, RefInt256 y) { - return cmp(x, y) > 0; -} - -bool operator<=(RefInt256 x, RefInt256 y) { - return cmp(x, y) <= 0; -} - -bool operator>=(RefInt256 x, RefInt256 y) { - return cmp(x, y) >= 0; -} - extern RefInt256 make_refint(long long x) { auto xx = td::RefInt256{true, x}; xx.unique_write().normalize(); diff --git a/ton-test-liteclient-full/lite-client/crypto/common/refint.h b/ton-test-liteclient-full/lite-client/crypto/common/refint.h index b1bbd40..9b4d34f 100644 --- a/ton-test-liteclient-full/lite-client/crypto/common/refint.h +++ b/ton-test-liteclient-full/lite-client/crypto/common/refint.h @@ -48,12 +48,35 @@ extern RefInt256& operator^=(RefInt256& x, RefInt256 y); extern RefInt256& operator<<=(RefInt256& x, int y); extern RefInt256& operator>>=(RefInt256& x, int y); -extern bool operator==(RefInt256 x, RefInt256 y); -extern bool operator!=(RefInt256 x, RefInt256 y); -extern bool operator<(RefInt256 x, RefInt256 y); -extern bool operator>(RefInt256 x, RefInt256 y); -extern bool operator<=(RefInt256 x, RefInt256 y); -extern bool operator>=(RefInt256 x, RefInt256 y); +template +bool operator==(RefInt256 x, T y) { + return cmp(x, y) == 0; +} + +template +bool operator!=(RefInt256 x, T y) { + return cmp(x, y) != 0; +} + +template +bool operator<(RefInt256 x, T y) { + return cmp(x, y) < 0; +} + +template +bool operator>(RefInt256 x, T y) { + return cmp(x, y) > 0; +} + +template +bool operator<=(RefInt256 x, T y) { + return cmp(x, y) <= 0; +} + +template +bool operator>=(RefInt256 x, T y) { + return cmp(x, y) >= 0; +} extern int cmp(RefInt256 x, RefInt256 y); extern int cmp(RefInt256 x, long long y); diff --git a/ton-test-liteclient-full/lite-client/crypto/func/a8.fc b/ton-test-liteclient-full/lite-client/crypto/func/a8.fc new file mode 100644 index 0000000..1cd18a3 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/a8.fc @@ -0,0 +1,28 @@ +int A(int a, int b, int c, int d, int e, int f) { + return muldiv(a, b, c) + muldiv(d, e, f); +} + +int B(int a, int b, int c, int d, int e, int f) { + return muldiv(a, b, c) + muldiv(b, d, e) + f; +} + +int C(int a, int b, int c, int d, int e, int f) { + return muldiv(a, b, c) + muldiv(b, d, c) + muldiv(d, d, f); +} + +int D(int a, int b, int c, int d, int e, int f) { + return muldiv(a, b, c) + muldiv(b, d, c) + muldiv(e, e, f); +} + +int E(int a, int b, int c, int d, int e, int f) { + return muldiv(a, b, c) + muldiv(c, d, e) + muldiv(c, c, f); +} + +int F(int a, int b, int c, int d, int e, int f) { + return muldiv(a, b, c) + muldiv(a, d, c) + muldiv(f, f, e); +} + +int G(int a, int b, int c, int d, int e, int f) { + return muldiv(a, b, c) + muldiv(b, c, d) + muldiv(b, c, e) + f; +} + diff --git a/ton-test-liteclient-full/lite-client/crypto/func/a9.fc b/ton-test-liteclient-full/lite-client/crypto/func/a9.fc new file mode 100644 index 0000000..e9cc145 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/a9.fc @@ -0,0 +1,23 @@ +int f(int x) { + if (2 * x + 1 == 7) { + return x + 17; + } else { + return 239; + } +} + +int f2(int x) { + return 2 * x + 1 == 6 ? x + 17 : 239; +} + +int g(int x) { + return x & 1 ? 3 * x + 1 : x / 2; +} + +_ H(int x, int y) { + return (x < y, x <= y, x == y, x >= y, x > y, x != y, x <=> y); +} + +int q() { + return 4; +} diff --git a/ton-test-liteclient-full/lite-client/crypto/func/a9_1.fc b/ton-test-liteclient-full/lite-client/crypto/func/a9_1.fc new file mode 100644 index 0000000..0dc9de0 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/a9_1.fc @@ -0,0 +1,28 @@ +_ F(int x) { + x = 2; + return x + 1; +} + +_ G(x) { + var y = x + 1; + x = 2; + return (x - 1, y); +} + +_ H(x) { + var y = x + 1; + x = 2; + return (x * y, y); +} + +_ I(x) { + int y = 17; + int z = x - y; + return (z, y); +} + +_ J(x) { + int y = 239; + int z = x - y; + return (z, y); +} diff --git a/ton-test-liteclient-full/lite-client/crypto/func/abscode.cpp b/ton-test-liteclient-full/lite-client/crypto/func/abscode.cpp new file mode 100644 index 0000000..e114eb5 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/abscode.cpp @@ -0,0 +1,474 @@ +#include "func.h" + +namespace funC { + +/* + * + * ABSTRACT CODE + * + */ + +TmpVar::TmpVar(var_idx_t _idx, int _cls, TypeExpr* _type, SymDef* sym, const SrcLocation* loc) + : v_type(_type), idx(_idx), cls(_cls), coord(0) { + if (sym) { + name = sym->sym_idx; + sym->value->idx = _idx; + } + if (loc) { + where = std::make_unique(*loc); + } + if (!_type) { + v_type = TypeExpr::new_hole(); + } +} + +void TmpVar::set_location(const SrcLocation& loc) { + if (where) { + *where = loc; + } else { + where = std::make_unique(loc); + } +} + +void TmpVar::dump(std::ostream& os) const { + show(os); + os << " : " << v_type << " (width "; + v_type->show_width(os); + os << ")"; + if (coord > 0) { + os << " = _" << (coord >> 8) << '.' << (coord & 255); + } else if (coord < 0) { + int n = (~coord >> 8), k = (~coord & 0xff); + if (k) { + os << " = (_" << n << ".._" << (n + k - 1) << ")"; + } else { + os << " = ()"; + } + } + os << std::endl; +} + +void TmpVar::show(std::ostream& os, int omit_idx) const { + if (cls & _Named) { + os << sym::symbols.get_name(name); + if (omit_idx && (omit_idx >= 2 || (cls & _UniqueName))) { + return; + } + } + os << '_' << idx; +} + +std::ostream& operator<<(std::ostream& os, const TmpVar& var) { + var.show(os); + return os; +} + +void VarDescr::show_value(std::ostream& os) const { + if (val & _Int) { + os << 'i'; + } + if (val & _Const) { + os << 'c'; + } + if (val & _Zero) { + os << '0'; + } + if (val & _NonZero) { + os << '!'; + } + if (val & _Pos) { + os << '>'; + } + if (val & _Neg) { + os << '<'; + } + if (val & _Bool) { + os << 'B'; + } + if (val & _Bit) { + os << 'b'; + } + if (val & _Even) { + os << 'E'; + } + if (val & _Odd) { + os << 'O'; + } + if (val & _Finite) { + os << 'f'; + } + if (val & _Nan) { + os << 'N'; + } + if (int_const.not_null()) { + os << '=' << int_const; + } +} + +void VarDescr::show(std::ostream& os, const char* name) const { + if (flags & _Last) { + os << '*'; + } + if (flags & _Unused) { + os << '?'; + } + if (name) { + os << name; + } + os << '_' << idx; + show_value(os); +} + +void VarDescr::set_const(long long value) { + return set_const(td::RefInt256{true, value}); +} + +void VarDescr::set_const(td::RefInt256 value) { + int_const = std::move(value); + if (!int_const->signed_fits_bits(257)) { + int_const.write().invalidate(); + } + val = _Const | _Int; + int s = sgn(int_const); + if (s < -1) { + val |= _Nan | _NonZero; + } else if (s < 0) { + val |= _NonZero | _Neg | _Finite; + if (*int_const == -1) { + val |= _Bool; + } + } else if (s > 0) { + val |= _NonZero | _Pos | _Finite; + } else if (!s) { + if (*int_const == 1) { + val |= _Bit; + } + val |= _Zero | _Neg | _Pos | _Finite | _Bool | _Bit; + } + if (val & _Finite) { + val |= int_const->get_bit(0) ? _Odd : _Even; + } +} + +void VarDescr::set_const_nan() { + set_const(td::RefInt256{true}); +} + +void VarDescr::operator|=(const VarDescr& y) { + val &= y.val; + if (is_int_const() && cmp(int_const, y.int_const) != 0) { + val &= ~_Const; + } + if (!(val & _Const)) { + int_const.clear(); + } +} + +void VarDescr::operator&=(const VarDescr& y) { + val |= y.val; + if (y.int_const.not_null() && int_const.is_null()) { + int_const = y.int_const; + } +} + +void VarDescr::set_value(const VarDescr& y) { + val = y.val; + int_const = y.int_const; +} + +void VarDescr::set_value(VarDescr&& y) { + val = y.val; + int_const = std::move(y.int_const); +} + +void VarDescr::clear_value() { + val = 0; + int_const.clear(); +} + +void VarDescrList::show(std::ostream& os) const { + os << "["; + for (const auto& v : list) { + os << ' ' << v; + } + os << " ]\n"; +} + +void Op::flags_set_clear(int set, int clear) { + flags = (flags | set) & ~clear; + for (auto& op : block0) { + op.flags_set_clear(set, clear); + } + for (auto& op : block1) { + op.flags_set_clear(set, clear); + } +} +void Op::split_vars(const std::vector& vars) { + split_var_list(left, vars); + split_var_list(right, vars); + for (auto& op : block0) { + op.split_vars(vars); + } + for (auto& op : block1) { + op.split_vars(vars); + } +} + +void Op::split_var_list(std::vector& var_list, const std::vector& vars) { + int new_size = 0, changes = 0; + for (var_idx_t v : var_list) { + int c = vars.at(v).coord; + if (c < 0) { + ++changes; + new_size += (~c & 0xff); + } else { + ++new_size; + } + } + if (!changes) { + return; + } + std::vector new_var_list; + new_var_list.reserve(new_size); + for (var_idx_t v : var_list) { + int c = vars.at(v).coord; + if (c < 0) { + int n = (~c >> 8), k = (~c & 0xff); + while (k-- > 0) { + new_var_list.push_back(n++); + } + } else { + new_var_list.push_back(v); + } + } + var_list = std::move(new_var_list); +} + +void Op::show(std::ostream& os, const std::vector& vars, std::string pfx, int mode) const { + if (mode & 2) { + os << pfx << " ["; + for (const auto& v : var_info.list) { + os << ' '; + if (v.flags & VarDescr::_Last) { + os << '*'; + } + if (v.flags & VarDescr::_Unused) { + os << '?'; + } + os << vars[v.idx]; + if (mode & 4) { + os << ':'; + v.show_value(os); + } + } + os << " ]\n"; + } + std::string dis = disabled() ? " " : ""; + if (noreturn()) { + dis += " "; + } + if (!is_pure()) { + dis += " "; + } + switch (cl) { + case _Undef: + os << pfx << dis << "???\n"; + break; + case _Nop: + os << pfx << dis << "NOP\n"; + break; + case _Call: + os << pfx << dis << "CALL: "; + show_var_list(os, left, vars); + os << " := " << (fun_ref ? fun_ref->name() : "(null)") << " "; + if ((mode & 4) && args.size() == right.size()) { + show_var_list(os, args, vars); + } else { + show_var_list(os, right, vars); + } + os << std::endl; + break; + case _CallInd: + os << pfx << dis << "CALLIND: "; + show_var_list(os, left, vars); + os << " := EXEC "; + show_var_list(os, right, vars); + os << std::endl; + break; + case _Let: + os << pfx << dis << "LET "; + show_var_list(os, left, vars); + os << " := "; + show_var_list(os, right, vars); + os << std::endl; + break; + case _IntConst: + os << pfx << dis << "CONST "; + show_var_list(os, left, vars); + os << " := " << int_const << std::endl; + break; + case _Import: + os << pfx << dis << "IMPORT "; + show_var_list(os, left, vars); + os << std::endl; + break; + case _Return: + os << pfx << dis << "RETURN "; + show_var_list(os, left, vars); + os << std::endl; + break; + case _GlobVar: + os << pfx << dis << "GLOBVAR "; + show_var_list(os, left, vars); + os << " := " << (fun_ref ? fun_ref->name() : "(null)") << std::endl; + break; + case _Repeat: + os << pfx << dis << "REPEAT "; + show_var_list(os, left, vars); + os << ' '; + show_block(os, block0.get(), vars, pfx, mode); + os << std::endl; + break; + case _If: + os << pfx << dis << "IF "; + show_var_list(os, left, vars); + os << ' '; + show_block(os, block0.get(), vars, pfx, mode); + os << " ELSE "; + show_block(os, block1.get(), vars, pfx, mode); + os << std::endl; + break; + case _While: + os << pfx << dis << "WHILE "; + show_var_list(os, left, vars); + os << ' '; + show_block(os, block0.get(), vars, pfx, mode); + os << " DO "; + show_block(os, block1.get(), vars, pfx, mode); + os << std::endl; + break; + case _Until: + os << pfx << dis << "UNTIL "; + show_var_list(os, left, vars); + os << ' '; + show_block(os, block0.get(), vars, pfx, mode); + os << std::endl; + break; + case _Again: + os << pfx << dis << "AGAIN "; + show_var_list(os, left, vars); + os << ' '; + show_block(os, block0.get(), vars, pfx, mode); + os << std::endl; + break; + default: + os << pfx << dis << " "; + show_var_list(os, left, vars); + os << " -- "; + show_var_list(os, right, vars); + os << std::endl; + break; + } +} + +void Op::show_var_list(std::ostream& os, const std::vector& idx_list, + const std::vector& vars) const { + if (!idx_list.size()) { + os << "()"; + } else if (idx_list.size() == 1) { + os << vars.at(idx_list[0]); + } else { + os << "(" << vars.at(idx_list[0]); + for (std::size_t i = 1; i < idx_list.size(); i++) { + os << "," << vars.at(idx_list[i]); + } + os << ")"; + } +} + +void Op::show_var_list(std::ostream& os, const std::vector& list, const std::vector& vars) const { + auto n = list.size(); + if (!n) { + os << "()"; + } else { + os << "( "; + for (std::size_t i = 0; i < list.size(); i++) { + if (i) { + os << ", "; + } + if (list[i].is_unused()) { + os << '?'; + } + os << vars.at(list[i].idx) << ':'; + list[i].show_value(os); + } + os << " )"; + } +} + +void Op::show_block(std::ostream& os, const Op* block, const std::vector& vars, std::string pfx, int mode) { + os << "{" << std::endl; + std::string pfx2 = pfx + " "; + for (const Op& op : block) { + op.show(os, vars, pfx2, mode); + } + os << pfx << "}"; +} + +void CodeBlob::flags_set_clear(int set, int clear) { + for (auto& op : ops) { + op.flags_set_clear(set, clear); + } +} + +std::ostream& operator<<(std::ostream& os, const CodeBlob& code) { + code.print(os); + return os; +} + +// flags: +1 = show variable definition locations; +2 = show vars after each op; +4 = show var abstract value info after each op; +8 = show all variables at start +void CodeBlob::print(std::ostream& os, int flags) const { + os << "CODE BLOB: " << var_cnt << " variables, " << in_var_cnt << " input\n"; + if ((flags & 8) != 0) { + for (const auto& var : vars) { + var.dump(os); + if (var.where && (flags & 1) != 0) { + var.where->show(os); + os << " defined here:\n"; + var.where->show_context(os); + } + } + } + os << "------- BEGIN --------\n"; + for (const auto& op : ops) { + op.show(os, vars, "", flags); + } + os << "-------- END ---------\n\n"; +} + +var_idx_t CodeBlob::create_var(int cls, TypeExpr* var_type, SymDef* sym, const SrcLocation* location) { + vars.emplace_back(var_cnt, cls, var_type, sym, location); + if (sym) { + sym->value->idx = var_cnt; + } + return var_cnt++; +} + +bool CodeBlob::import_params(FormalArgList arg_list) { + if (var_cnt || in_var_cnt || op_cnt) { + return false; + } + std::vector list; + for (const auto& par : arg_list) { + TypeExpr* arg_type; + SymDef* arg_sym; + SrcLocation arg_loc; + std::tie(arg_type, arg_sym, arg_loc) = par; + list.push_back(create_var(arg_sym ? (TmpVar::_In | TmpVar::_Named) : TmpVar::_In, arg_type, arg_sym, &arg_loc)); + } + emplace_back(loc, Op::_Import, list); + in_var_cnt = var_cnt; + return true; +} + +} // namespace funC diff --git a/ton-test-liteclient-full/lite-client/crypto/func/analyzer.cpp b/ton-test-liteclient-full/lite-client/crypto/func/analyzer.cpp new file mode 100644 index 0000000..b3f9a19 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/analyzer.cpp @@ -0,0 +1,808 @@ +#include "func.h" + +namespace funC { + +/* + * + * ANALYZE AND PREPROCESS ABSTRACT CODE + * + */ + +void CodeBlob::simplify_var_types() { + for (TmpVar& var : vars) { + TypeExpr::remove_indirect(var.v_type); + } +} + +int CodeBlob::split_vars(bool strict) { + int n = var_cnt, changes = 0; + for (int j = 0; j < var_cnt; j++) { + TmpVar& var = vars[j]; + if (strict && var.v_type->minw != var.v_type->maxw) { + throw src::ParseError{var.where.get(), "variable does not have fixed width, cannot manipulate it"}; + } + std::vector comp_types; + int k = var.v_type->extract_components(comp_types); + assert(k <= 254 && n <= 0x7fff00); + assert((unsigned)k == comp_types.size()); + if (k != 1) { + var.coord = ~((n << 8) + k); + for (int i = 0; i < k; i++) { + auto v = create_var(vars[j].cls, comp_types[i], 0, vars[j].where.get()); + assert(v == n + i); + assert(vars[v].idx == v); + vars[v].name = vars[j].name; + vars[v].coord = ((int)j << 8) + i + 1; + } + n += k; + ++changes; + } else if (strict && var.v_type->minw != 1) { + throw src::ParseError{var.where.get(), + "cannot work with variable or variable component of width greater than one"}; + } + } + if (!changes) { + return 0; + } + for (auto& op : ops) { + op.split_vars(vars); + } + return changes; +} + +bool CodeBlob::compute_used_code_vars() { + VarDescrList empty_var_info; + return compute_used_code_vars(ops, empty_var_info, true); +} + +bool CodeBlob::compute_used_code_vars(std::unique_ptr& ops_ptr, const VarDescrList& var_info, bool edit) const { + assert(ops_ptr); + if (!ops_ptr->next) { + assert(ops_ptr->cl == Op::_Nop); + return ops_ptr->set_var_info(var_info); + } + return compute_used_code_vars(ops_ptr->next, var_info, edit) | ops_ptr->compute_used_vars(*this, edit); +} + +bool operator==(const VarDescrList& x, const VarDescrList& y) { + if (x.size() != y.size()) { + return false; + } + for (std::size_t i = 0; i < x.size(); i++) { + if (x.list[i].idx != y.list[i].idx || x.list[i].flags != y.list[i].flags) { + return false; + } + } + return true; +} + +bool same_values(const VarDescr& x, const VarDescr& y) { + if (x.val != y.val || x.int_const.is_null() != y.int_const.is_null()) { + return false; + } + if (x.int_const.not_null() && cmp(x.int_const, y.int_const) != 0) { + return false; + } + return true; +} + +bool same_values(const VarDescrList& x, const VarDescrList& y) { + if (x.size() != y.size()) { + return false; + } + for (std::size_t i = 0; i < x.size(); i++) { + if (x.list[i].idx != y.list[i].idx || !same_values(x.list[i], y.list[i])) { + return false; + } + } + return true; +} + +bool Op::set_var_info(const VarDescrList& new_var_info) { + if (var_info == new_var_info) { + return false; + } + var_info = new_var_info; + return true; +} + +bool Op::set_var_info(VarDescrList&& new_var_info) { + if (var_info == new_var_info) { + return false; + } + var_info = std::move(new_var_info); + return true; +} + +bool Op::set_var_info_except(const VarDescrList& new_var_info, const std::vector& var_list) { + if (!var_list.size()) { + return set_var_info(new_var_info); + } + VarDescrList tmp_info{new_var_info}; + tmp_info -= var_list; + return set_var_info(new_var_info); +} + +bool Op::set_var_info_except(VarDescrList&& new_var_info, const std::vector& var_list) { + if (var_list.size()) { + new_var_info -= var_list; + } + return set_var_info(std::move(new_var_info)); +} +std::vector sort_unique_vars(const std::vector& var_list) { + std::vector vars{var_list}, unique_vars; + std::sort(vars.begin(), vars.end()); + vars.erase(std::unique(vars.begin(), vars.end()), vars.end()); + return vars; +} + +VarDescr* VarDescrList::operator[](var_idx_t idx) { + auto it = std::lower_bound(list.begin(), list.end(), idx); + return it != list.end() && it->idx == idx ? &*it : nullptr; +} + +const VarDescr* VarDescrList::operator[](var_idx_t idx) const { + auto it = std::lower_bound(list.begin(), list.end(), idx); + return it != list.end() && it->idx == idx ? &*it : nullptr; +} + +std::size_t VarDescrList::count(const std::vector idx_list) const { + std::size_t res = 0; + for (var_idx_t idx : idx_list) { + if (operator[](idx)) { + ++res; + } + } + return res; +} + +std::size_t VarDescrList::count_used(const std::vector idx_list) const { + std::size_t res = 0; + for (var_idx_t idx : idx_list) { + auto v = operator[](idx); + if (v && !v->is_unused()) { + ++res; + } + } + return res; +} + +VarDescrList& VarDescrList::operator-=(var_idx_t idx) { + auto it = std::lower_bound(list.begin(), list.end(), idx); + if (it != list.end() && it->idx == idx) { + list.erase(it); + } + return *this; +} + +VarDescrList& VarDescrList::operator-=(const std::vector& idx_list) { + for (var_idx_t idx : idx_list) { + *this -= idx; + } + return *this; +} + +VarDescrList& VarDescrList::add_var(var_idx_t idx, bool unused) { + auto it = std::lower_bound(list.begin(), list.end(), idx); + if (it == list.end() || it->idx != idx) { + list.emplace(it, idx, VarDescr::_Last | (unused ? VarDescr::_Unused : 0)); + } else if (it->is_unused() && !unused) { + it->clear_unused(); + } + return *this; +} + +VarDescrList& VarDescrList::add_vars(const std::vector& idx_list, bool unused) { + for (var_idx_t idx : idx_list) { + add_var(idx, unused); + } + return *this; +} + +VarDescr& VarDescrList::add(var_idx_t idx) { + auto it = std::lower_bound(list.begin(), list.end(), idx); + if (it == list.end() || it->idx != idx) { + it = list.emplace(it, idx); + } + return *it; +} + +VarDescr& VarDescrList::add_newval(var_idx_t idx) { + auto it = std::lower_bound(list.begin(), list.end(), idx); + if (it == list.end() || it->idx != idx) { + return *list.emplace(it, idx); + } else { + it->clear_value(); + return *it; + } +} + +VarDescrList& VarDescrList::clear_last() { + for (auto& var : list) { + if (var.flags & VarDescr::_Last) { + var.flags &= ~VarDescr::_Last; + } + } + return *this; +} + +VarDescrList VarDescrList::operator+(const VarDescrList& y) const { + VarDescrList res; + auto it1 = list.cbegin(); + auto it2 = y.list.cbegin(); + while (it1 != list.cend() && it2 != y.list.cend()) { + if (it1->idx < it2->idx) { + res.list.push_back(*it1++); + } else if (it1->idx > it2->idx) { + res.list.push_back(*it2++); + } else { + res.list.push_back(*it1++); + res.list.back() += *it2++; + } + } + while (it1 != list.cend()) { + res.list.push_back(*it1++); + } + while (it2 != y.list.cend()) { + res.list.push_back(*it2++); + } + return res; +} + +VarDescrList& VarDescrList::operator+=(const VarDescrList& y) { + return *this = *this + y; +} + +VarDescrList VarDescrList::operator|(const VarDescrList& y) const { + VarDescrList res; + auto it1 = list.cbegin(); + auto it2 = y.list.cbegin(); + while (it1 != list.cend() && it2 != y.list.cend()) { + if (it1->idx < it2->idx) { + it1++; + } else if (it1->idx > it2->idx) { + it2++; + } else { + res.list.push_back(*it1++); + res.list.back() |= *it2++; + } + } + return res; +} + +VarDescrList& VarDescrList::operator|=(const VarDescrList& y) { + return *this = *this | y; +} + +VarDescrList& VarDescrList::operator&=(const VarDescrList& values) { + for (const VarDescr& vd : values.list) { + VarDescr* item = operator[](vd.idx); + if (item) { + *item &= vd; + } + } + return *this; +} + +VarDescrList& VarDescrList::import_values(const VarDescrList& values) { + for (const VarDescr& vd : values.list) { + VarDescr* item = operator[](vd.idx); + if (item) { + item->set_value(vd); + } + } + return *this; +} + +bool Op::std_compute_used_vars(bool disabled) { + // left = OP right + // var_info := (var_info - left) + right + VarDescrList new_var_info{next->var_info}; + new_var_info -= left; + new_var_info.clear_last(); + if (args.size() == right.size() && !disabled) { + for (const VarDescr& arg : args) { + new_var_info.add_var(arg.idx, arg.is_unused()); + } + } else { + new_var_info.add_vars(right, disabled); + } + return set_var_info(std::move(new_var_info)); +} + +bool Op::compute_used_vars(const CodeBlob& code, bool edit) { + assert(next); + const VarDescrList& next_var_info = next->var_info; + if (cl == _Nop) { + return set_var_info_except(next_var_info, left); + } + switch (cl) { + case _IntConst: + case _GlobVar: + case _Call: + case _CallInd: { + // left = EXEC right; + if (!next_var_info.count_used(left) && is_pure()) { + // all variables in `left` are not needed + if (edit) { + disable(); + } + return std_compute_used_vars(true); + } + return std_compute_used_vars(); + } + case _Let: { + // left = right + std::size_t cnt = next_var_info.count_used(left); + if (!cnt) { + // all variables in `left` are not needed + if (edit) { + disable(); + } + return std_compute_used_vars(true); + } else { + assert(left.size() == right.size()); + auto l_it = left.cbegin(), r_it = right.cbegin(); + VarDescrList new_var_info{next_var_info}; + new_var_info -= left; + new_var_info.clear_last(); + std::vector new_left, new_right; + for (; l_it < left.cend(); ++l_it, ++r_it) { + if (std::find(l_it + 1, left.cend(), *l_it) == left.cend()) { + auto p = next_var_info[*l_it]; + new_var_info.add_var(*r_it, !p || p->is_unused()); + new_left.push_back(*l_it); + new_right.push_back(*r_it); + } + } + if (new_left.size() < left.size()) { + left = std::move(new_left); + right = std::move(new_right); + } + return set_var_info(std::move(new_var_info)); + } + } + case _Return: { + // return left + if (var_info.count(left) == left.size()) { + return false; + } + std::vector unique_vars = sort_unique_vars(left); + var_info.list.clear(); + for (var_idx_t i : unique_vars) { + var_info.list.emplace_back(i, VarDescr::_Last); + } + return true; + } + case _Import: { + // import left + std::vector unique_vars = sort_unique_vars(left); + var_info.list.clear(); + for (var_idx_t i : unique_vars) { + var_info.list.emplace_back(i, next_var_info[i] ? 0 : VarDescr::_Last); + } + return true; + } + case _If: { + // if (left) then block0 else block1 + code.compute_used_code_vars(block0, next_var_info, edit); + code.compute_used_code_vars(block1, next_var_info, edit); + VarDescrList merge_info = block0->var_info + block1->var_info; + merge_info.clear_last(); + merge_info += left; + return set_var_info(std::move(merge_info)); + } + case _While: { + // while (block0 || left) block1; + // ... { block0 left block1 } block0 left next + VarDescrList after_cond_first{next_var_info}; + after_cond_first += left; + code.compute_used_code_vars(block0, after_cond_first, false); + VarDescrList new_var_info{block0->var_info}; + bool changes = false; + do { + code.compute_used_code_vars(block1, block0->var_info, changes); + VarDescrList after_cond{block1->var_info}; + after_cond += left; + code.compute_used_code_vars(block0, after_cond, changes); + std::size_t n = new_var_info.size(); + new_var_info += block0->var_info; + new_var_info.clear_last(); + if (changes) { + break; + } + changes = (new_var_info.size() == n); + } while (changes <= edit); + return set_var_info(std::move(new_var_info)); + } + case _Until: { + // until (block0 || left); + // .. { block0 left } block0 left next + VarDescrList after_cond_first{next_var_info}; + after_cond_first += left; + code.compute_used_code_vars(block0, after_cond_first, false); + VarDescrList new_var_info{block0->var_info}; + bool changes = false; + do { + VarDescrList after_cond{new_var_info}; + after_cond += next_var_info; + after_cond += left; + code.compute_used_code_vars(block0, after_cond, changes); + std::size_t n = new_var_info.size(); + new_var_info += block0->var_info; + new_var_info.clear_last(); + if (changes) { + break; + } + changes = (new_var_info.size() == n); + } while (changes <= edit); + return set_var_info(std::move(new_var_info) + next_var_info); + } + case _Repeat: { + // repeat (left) block0 + // left { block0 } next + VarDescrList new_var_info{next_var_info}; + bool changes = false; + do { + code.compute_used_code_vars(block0, new_var_info, changes); + std::size_t n = new_var_info.size(); + new_var_info += block0->var_info; + new_var_info.clear_last(); + if (changes) { + break; + } + changes = (new_var_info.size() == n); + } while (changes <= edit); + new_var_info += left; + return set_var_info(std::move(new_var_info)); + } + case _Again: { + // for(;;) block0 + // { block0 } + VarDescrList new_var_info; + bool changes = false; + do { + code.compute_used_code_vars(block0, new_var_info, changes); + std::size_t n = new_var_info.size(); + new_var_info += block0->var_info; + new_var_info.clear_last(); + if (changes) { + break; + } + changes = (new_var_info.size() == n); + } while (changes <= edit); + return set_var_info(std::move(new_var_info)); + } + default: + std::cerr << "fatal: unknown operation in compute_used_vars()\n"; + throw src::ParseError{where, "unknown operation"}; + } +} + +bool prune_unreachable(std::unique_ptr& ops) { + if (!ops) { + return true; + } + Op& op = *ops; + if (op.cl == Op::_Nop) { + if (op.next) { + ops = std::move(op.next); + return prune_unreachable(ops); + } + return true; + } + bool reach; + switch (op.cl) { + case Op::_IntConst: + case Op::_GlobVar: + case Op::_Call: + case Op::_CallInd: + case Op::_Import: + reach = true; + break; + case Op::_Let: { + reach = true; + break; + } + case Op::_Return: + reach = false; + break; + case Op::_If: { + // if left then block0 else block1; ... + VarDescr* c_var = op.var_info[op.left[0]]; + if (c_var && c_var->always_true()) { + op.block0->last().next = std::move(op.next); + ops = std::move(op.block0); + return prune_unreachable(ops); + } else if (c_var && c_var->always_false()) { + op.block1->last().next = std::move(op.next); + ops = std::move(op.block1); + return prune_unreachable(ops); + } else { + reach = prune_unreachable(op.block0) | prune_unreachable(op.block1); + } + break; + } + case Op::_While: { + // while (block0 || left) block1; + if (!prune_unreachable(op.block0)) { + // computation of block0 never returns + ops = std::move(op.block0); + return prune_unreachable(ops); + } + VarDescr* c_var = op.block0->last().var_info[op.left[0]]; + if (c_var && c_var->always_false()) { + // block1 never executed + op.block0->last().next = std::move(op.next); + ops = std::move(op.block0); + return false; + } else if (c_var && c_var->always_true()) { + if (!prune_unreachable(op.block1)) { + // block1 never returns + op.block0->last().next = std::move(op.block1); + ops = std::move(op.block0); + return false; + } + // infinite loop + op.cl = Op::_Again; + op.block0->last().next = std::move(op.block1); + op.left.clear(); + reach = false; + } else { + if (!prune_unreachable(op.block1)) { + // block1 never returns, while equivalent to block0 ; if left then block1 else next + op.cl = Op::_If; + std::unique_ptr new_op = std::move(op.block0); + op.block0 = std::move(op.block1); + op.block1 = std::make_unique(op.next->where, Op::_Nop); + new_op->last().next = std::move(ops); + ops = std::move(new_op); + } + reach = true; // block1 may be never executed + } + break; + } + case Op::_Repeat: { + // repeat (left) block0 + VarDescr* c_var = op.var_info[op.left[0]]; + if (c_var && c_var->always_nonpos()) { + // loop never executed + ops = std::move(op.next); + return prune_unreachable(ops); + } + if (c_var && c_var->always_pos()) { + if (!prune_unreachable(op.block0)) { + // block0 executed at least once, and it never returns + // replace code with block0 + ops = std::move(op.block0); + return false; + } + } else { + prune_unreachable(op.block0); + } + reach = true; + break; + } + case Op::_Until: + case Op::_Again: { + // do block0 until left; ... + if (!prune_unreachable(op.block0)) { + // block0 never returns, replace loop by block0 + ops = std::move(op.block0); + return false; + } + reach = true; + break; + } + default: + std::cerr << "fatal: unknown operation \n"; + throw src::ParseError{op.where, "unknown operation in prune_unreachable()"}; + } + if (reach) { + return prune_unreachable(op.next); + } else { + while (op.next->next) { + op.next = std::move(op.next->next); + } + return false; + } +} + +void CodeBlob::prune_unreachable_code() { + if (prune_unreachable(ops)) { + throw src::ParseError{loc, "control reaches end of function"}; + } +} + +void CodeBlob::fwd_analyze() { + VarDescrList values; + assert(ops && ops->cl == Op::_Import); + for (var_idx_t i : ops->left) { + values += i; + if (vars[i].v_type->is_int()) { + values[i]->val |= VarDescr::_Int; + } + } + ops->fwd_analyze(values); +} + +void Op::prepare_args(VarDescrList values) { + if (args.size() != right.size()) { + args.clear(); + for (var_idx_t i : right) { + args.emplace_back(i); + } + } + for (std::size_t i = 0; i < right.size(); i++) { + const VarDescr* val = values[right[i]]; + if (val) { + args[i].set_value(*val); + args[i].clear_unused(); + } + } +} + +VarDescrList Op::fwd_analyze(VarDescrList values) { + var_info.import_values(values); + switch (cl) { + case _Nop: + case _Import: + break; + case _Return: + values.list.clear(); + break; + case _IntConst: { + values.add_newval(left[0]).set_const(int_const); + break; + } + case _GlobVar: + case _Call: { + prepare_args(values); + auto func = dynamic_cast(fun_ref->value); + if (func) { + std::vector res; + res.reserve(left.size()); + for (var_idx_t i : left) { + res.emplace_back(i); + } + func->compile(res, args); // abstract interpretation of res := f (args) + int j = 0; + for (var_idx_t i : left) { + values.add_newval(i).set_value(res[j++]); + } + } else { + for (var_idx_t i : left) { + values.add_newval(i); + } + } + break; + } + case _CallInd: { + for (var_idx_t i : left) { + values.add_newval(i); + } + break; + } + case _Let: { + std::vector old_val; + assert(left.size() == right.size()); + for (std::size_t i = 0; i < right.size(); i++) { + const VarDescr* ov = values[right[i]]; + assert(ov); + old_val.push_back(*ov); + } + for (std::size_t i = 0; i < left.size(); i++) { + values.add_newval(left[i]).set_value(std::move(old_val[i])); + } + break; + } + case _If: { + VarDescrList val1 = block0->fwd_analyze(values); + VarDescrList val2 = block1->fwd_analyze(std::move(values)); + values = val1 | val2; + break; + } + case _Repeat: { + bool atl1 = (values[left[0]] && values[left[0]]->always_pos()); + VarDescrList next_values = block0->fwd_analyze(values); + while (true) { + VarDescrList new_values = values | next_values; + if (same_values(new_values, values)) { + break; + } + values = std::move(new_values); + next_values = block0->fwd_analyze(values); + } + if (atl1) { + values = std::move(next_values); + } + break; + } + case _While: { + values = block0->fwd_analyze(values); + if (values[left[0]] && values[left[0]]->always_false()) { + // block1 never executed + block1->fwd_analyze(values); + break; + } + while (true) { + VarDescrList next_values = values | block0->fwd_analyze(block1->fwd_analyze(values)); + if (same_values(next_values, values)) { + break; + } + values = std::move(next_values); + } + break; + } + case _Until: + case _Again: { + while (true) { + VarDescrList next_values = values | block0->fwd_analyze(values); + if (same_values(next_values, values)) { + break; + } + values = std::move(next_values); + } + values = block0->fwd_analyze(values); + break; + } + default: + std::cerr << "fatal: unknown operation \n"; + throw src::ParseError{where, "unknown operation in fwd_analyze()"}; + } + if (next) { + return next->fwd_analyze(std::move(values)); + } else { + return values; + } +} + +bool Op::set_noreturn(bool nr) { + if (nr) { + flags |= _NoReturn; + } else { + flags &= ~_NoReturn; + } + return nr; +} + +bool Op::mark_noreturn() { + switch (cl) { + case _Nop: + if (!next) { + return set_noreturn(false); + } + // fallthrough + case _Import: + case _IntConst: + case _Let: + case _GlobVar: + case _CallInd: + case _Call: + return set_noreturn(next->mark_noreturn()); + case _Return: + return set_noreturn(true); + case _If: + return set_noreturn((block0->mark_noreturn() & block1->mark_noreturn()) | next->mark_noreturn()); + case _Again: + block0->mark_noreturn(); + return set_noreturn(false); + case _Until: + return set_noreturn(block0->mark_noreturn() | next->mark_noreturn()); + case _While: + block1->mark_noreturn(); + return set_noreturn(block0->mark_noreturn() | next->mark_noreturn()); + case _Repeat: + block0->mark_noreturn(); + return set_noreturn(next->mark_noreturn()); + default: + std::cerr << "fatal: unknown operation \n"; + throw src::ParseError{where, "unknown operation in mark_noreturn()"}; + } +} + +void CodeBlob::mark_noreturn() { + ops->mark_noreturn(); +} + +} // namespace funC diff --git a/ton-test-liteclient-full/lite-client/crypto/func/asmops.cpp b/ton-test-liteclient-full/lite-client/crypto/func/asmops.cpp new file mode 100644 index 0000000..5e07606 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/asmops.cpp @@ -0,0 +1,260 @@ +#include "parser/srcread.h" +#include "func.h" +#include + +namespace funC { + +/* + * + * ASM-OP LIST FUNCTIONS + * + */ + +int is_pos_pow2(td::RefInt256 x) { + if (sgn(x) > 0 && !sgn(x & (x - 1))) { + return x->bit_size(false) - 1; + } else { + return -1; + } +} + +int is_neg_pow2(td::RefInt256 x) { + return sgn(x) < 0 ? is_pos_pow2(-x) : 0; +} + +AsmOp AsmOp::Const(int arg, std::string push_op) { + std::ostringstream os; + os << arg << ' ' << push_op; + return AsmOp::Custom(os.str()); +} + +AsmOp AsmOp::make_stk2(int a, int b, const char* str, int delta) { + std::ostringstream os; + os << "s" << a << " s" << b << " " << str; + int c = std::max(a, b) + 1; + return AsmOp::Custom(os.str(), c, c + delta); +} + +AsmOp AsmOp::make_stk3(int a, int b, int c, const char* str, int delta) { + std::ostringstream os; + os << "s" << a << " s" << b << " s" << c << " " << str; + int m = std::max(a, std::max(b, c)) + 1; + return AsmOp::Custom(os.str(), m, m + delta); +} + +AsmOp AsmOp::BlkSwap(int a, int b) { + std::ostringstream os; + if (a == 1 && b == 1) { + return AsmOp::Xchg(0, 1); + } else if (a == 1) { + if (b == 2) { + os << "ROT"; + } else { + os << b << " ROLL"; + } + } else if (b == 1) { + if (a == 2) { + os << "-ROT"; + } else { + os << a << " -ROLL"; + } + } else { + os << a << " " << b << " BLKSWAP"; + } + return AsmOp::Custom(os.str(), a + b, a + b); +} + +AsmOp AsmOp::BlkPush(int a, int b) { + std::ostringstream os; + if (a == 1) { + return AsmOp::Push(b); + } else if (a == 2 && b == 1) { + os << "2DUP"; + } else { + os << a << " " << b << " BLKPUSH"; + } + return AsmOp::Custom(os.str(), b + 1, a + b + 1); +} + +AsmOp AsmOp::BlkDrop(int a) { + std::ostringstream os; + if (a == 1) { + return AsmOp::Pop(); + } else if (a == 2) { + os << "2DROP"; + } else { + os << a << " BLKDROP"; + } + return AsmOp::Custom(os.str(), a, 0); +} + +AsmOp AsmOp::BlkReverse(int a, int b) { + std::ostringstream os; + os << a << " " << b << " REVERSE"; + return AsmOp::Custom(os.str(), a + b, a + b); +} + +AsmOp AsmOp::IntConst(td::RefInt256 x) { + if (x->signed_fits_bits(8)) { + return AsmOp::Const(dec_string(std::move(x)) + " PUSHINT"); + } + if (!x->is_valid()) { + return AsmOp::Const("PUSHNAN"); + } + int k = is_pos_pow2(x); + if (k >= 0) { + return AsmOp::Const(k, "PUSHPOW2"); + } + k = is_pos_pow2(x + 1); + if (k >= 0) { + return AsmOp::Const(k, "PUSHPOW2DEC"); + } + k = is_pos_pow2(-x); + if (k >= 0) { + return AsmOp::Const(k, "PUSHNEGPOW2"); + } + return AsmOp::Const(dec_string(std::move(x)) + " PUSHINT"); +} + +void AsmOp::out(std::ostream& os) const { + if (!op.empty()) { + os << op; + return; + } + switch (t) { + case a_none: + break; + case a_xchg: + if (!a && !(b & -2)) { + os << (b ? "SWAP" : "NOP"); + break; + } + os << "s" << a << " s" << b << " XCHG"; + break; + case a_push: + if (!(a & -2)) { + os << (a ? "OVER" : "DUP"); + break; + } + os << "s" << a << " PUSH"; + break; + case a_pop: + if (!(a & -2)) { + os << (a ? "NIP" : "DROP"); + break; + } + os << "s" << a << " POP"; + break; + default: + throw src::Fatal{"unknown assembler operation"}; + } +} + +void AsmOp::out_indent_nl(std::ostream& os, bool no_eol) const { + for (int i = 0; i < indent; i++) { + os << " "; + } + out(os); + if (!no_eol) { + os << std::endl; + } +} + +std::string AsmOp::to_string() const { + if (!op.empty()) { + return op; + } else { + std::ostringstream os; + out(os); + return os.str(); + } +} + +const_idx_t AsmOpList::register_const(Const new_const) { + if (new_const.is_null()) { + return not_const; + } + unsigned idx; + for (idx = 0; idx < constants_.size(); idx++) { + if (!td::cmp(new_const, constants_[idx])) { + return idx; + } + } + constants_.push_back(std::move(new_const)); + return (const_idx_t)idx; +} + +Const AsmOpList::get_const(const_idx_t idx) { + if ((unsigned)idx < constants_.size()) { + return constants_[idx]; + } else { + return {}; + } +} + +void AsmOpList::show_var(std::ostream& os, var_idx_t idx) const { + if (!var_names_ || (unsigned)idx >= var_names_->size()) { + os << '_' << idx; + } else { + var_names_->at(idx).show(os, 2); + } +} + +void AsmOpList::show_var_ext(std::ostream& os, std::pair idx_pair) const { + auto i = idx_pair.first; + auto j = idx_pair.second; + if (!var_names_ || (unsigned)i >= var_names_->size()) { + os << '_' << i; + } else { + var_names_->at(i).show(os, 2); + } + if ((unsigned)j < constants_.size() && constants_[j].not_null()) { + os << '=' << constants_[j]; + } +} + +void AsmOpList::out(std::ostream& os, int mode) const { + if (!(mode & 2)) { + for (const auto& op : list_) { + op.out_indent_nl(os); + } + } else { + std::size_t n = list_.size(); + for (std::size_t i = 0; i < n; i++) { + const auto& op = list_[i]; + if (!op.is_comment() && i + 1 < n && list_[i + 1].is_comment()) { + op.out_indent_nl(os, true); + os << '\t'; + do { + i++; + } while (i + 1 < n && list_[i + 1].is_comment()); + list_[i].out(os); + os << std::endl; + } else { + op.out_indent_nl(os, false); + } + } + } +} + +bool apply_op(StackTransform& trans, const AsmOp& op) { + if (!trans.is_valid()) { + return false; + } + switch (op.t) { + case AsmOp::a_none: + return true; + case AsmOp::a_xchg: + return trans.apply_xchg(op.a, op.b, true); + case AsmOp::a_push: + return trans.apply_push(op.a); + case AsmOp::a_pop: + return trans.apply_pop(op.a); + case AsmOp::a_const: + return !op.a && op.b == 1 && trans.apply_push_newconst(); + default: + return false; + } +} + +} // namespace funC diff --git a/ton-test-liteclient-full/lite-client/crypto/func/b1.fc b/ton-test-liteclient-full/lite-client/crypto/func/b1.fc new file mode 100644 index 0000000..ce52293 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/b1.fc @@ -0,0 +1,63 @@ +int now() asm "NOW"; + +int cell_hash(cell c) +asm "HASHCU"; + +int slice_hash(slice s) +asm "HASHSU"; + +int check_signature(int hash, slice signature, int public_key) +asm "CHKSIGNU"; + +;; () throw_if(int excno, int cond) impure +;; asm "THROWARGIF"; + +cell get_data() asm "c4 PUSH"; +() set_data(cell c) impure asm "c4 POP"; +() accept_message() impure asm "ACCEPT"; + +slice begin_parse(cell c) asm "CTOS"; +() end_parse(slice s) impure asm "ENDS"; +(cell, slice) load_ref(slice s) asm "LDREF"; +(int, slice) zload_int(slice s, int len) asm "LDIX"; +(int, slice) zload_uint(slice s, int len) asm "LDUX"; +int zpreload_int(slice s, int len) asm "PLDIX"; +int zpreload_uint(slice s, int len) asm "PLDUX"; +(slice, slice) load_bits(slice s, int len) asm "LDSLICEX"; +slice preload_bits(slice s, int len) asm "PLDSLICEX"; +cell set_idict_ref(cell value, int index, cell dict, int key_len) asm "DICTISETREF"; +builder begin_cell() asm "NEWC"; +builder store_ref(cell c, builder b) asm "STREF"; +builder zstore_uint(int x, builder b, int len) asm "STUX"; +builder zstore_int(int x, builder b, int len) asm "STIX"; +cell end_cell(builder b) asm "ENDC"; + +;; Simple configuration smart contract + +() recv_internal(cell in_msg) impure { + ;; do nothing for internal messages +} + +() recv_external(cell in_msg) impure { + var (signature, cs0) = load_bits(begin_parse(in_msg), 512); + var (msg_seqno, cs) = zload_uint(cs0, 32); + (var valid_until, cs) = zload_uint(cs, 32); + throw_if(35, valid_until < now()); + var (cfg_dict, cs2) = load_ref(begin_parse(get_data())); + (var stored_seqno, cs2) = zload_uint(cs2, 32); + (var public_key, cs2) = zload_uint(cs2, 256); + end_parse(cs2); + throw_unless(33, msg_seqno == stored_seqno); + throw_unless(34, check_signature(slice_hash(cs0), signature, public_key)); + accept_message(); + (var param_index, cs) = zload_uint(cs, 32); + (var param_value, cs) = load_ref(cs); + end_parse(cs); +;; cfg_dict = set_idict_ref(param_value, param_index, cfg_dict, 32); +;; var cb = begin_cell(); +;; cb = store_ref(cfg_dict, cb); + var cb = store_ref(set_idict_ref(param_value, param_index, cfg_dict, 32), begin_cell()); + cb = zstore_uint(stored_seqno + 1, cb, 32); + cb = zstore_uint(public_key, cb, 256); + set_data(end_cell(cb)); +} diff --git a/ton-test-liteclient-full/lite-client/crypto/func/b2.fc b/ton-test-liteclient-full/lite-client/crypto/func/b2.fc new file mode 100644 index 0000000..90322ae --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/b2.fc @@ -0,0 +1,60 @@ +int now() asm "NOW"; + +int cell_hash(cell c) +asm "HASHCU"; + +int slice_hash(slice s) +asm "HASHSU"; + +int check_signature(int hash, slice signature, int public_key) +asm "CHKSIGNU"; + +;; () throw_if(int excno, int cond) impure +;; asm "THROWARGIF"; + +cell get_data() asm "c4 PUSH"; +() set_data(cell c) impure asm "c4 POP"; +() accept_message() impure asm "ACCEPT"; + +slice begin_parse(cell c) asm "CTOS"; +() end_parse(slice s) impure asm "ENDS"; +(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF"; +;; (slice, int) load_int(slice s, int len) asm(s len -> 1 0) "LDIX"; +;; (slice, int) load_uint(slice s, int len) asm( -> 1 0) "LDUX"; +;; int preload_int(slice s, int len) asm "PLDIX"; +;; int preload_uint(slice s, int len) asm "PLDUX"; +(slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX"; +slice preload_bits(slice s, int len) asm "PLDSLICEX"; +cell set_idict_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; +builder begin_cell() asm "NEWC"; +builder store_ref(builder b, cell c) asm(c b) "STREF"; +;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX"; +;; builder store_int(builder b, int x, int len) asm(x b len) "STIX"; +cell end_cell(builder b) asm "ENDC"; + +;; Simple configuration smart contract + +() recv_internal(cell in_msg) impure { + ;; do nothing for internal messages +} + +() recv_external(cell in_msg) impure { + var (cs0, signature) = load_bits(begin_parse(in_msg), 512); + var (cs, msg_seqno) = load_uint(cs0, 32); + (cs, var valid_until) = load_uint(cs, 32); + throw_if(35, valid_until < now()); + var (cs2, cfg_dict) = load_ref(begin_parse(get_data())); + (cs2, var stored_seqno) = load_uint(cs2, 32); + (cs2, var public_key) = load_uint(cs2, 256); + end_parse(cs2); + throw_unless(33, msg_seqno == stored_seqno); + throw_unless(34, check_signature(slice_hash(cs0), signature, public_key)); + accept_message(); + (cs, var param_index) = load_uint(cs, 32); + (cs, var param_value) = load_ref(cs); + end_parse(cs); + var cb = store_ref(begin_cell(), set_idict_ref(cfg_dict, 32, param_index, param_value)); + cb = store_uint(cb, stored_seqno + 1, 32); + cb = store_uint(cb, public_key, 256); + set_data(end_cell(cb)); +} diff --git a/ton-test-liteclient-full/lite-client/crypto/func/b2_0.fc b/ton-test-liteclient-full/lite-client/crypto/func/b2_0.fc new file mode 100644 index 0000000..c2783f8 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/b2_0.fc @@ -0,0 +1,63 @@ +int now() asm "NOW"; + +int cell_hash(cell c) +asm "HASHCU"; + +int slice_hash(slice s) +asm "HASHSU"; + +int check_signature(int hash, slice signature, int public_key) +asm "CHKSIGNU"; + +;; () throw_if(int excno, int cond) impure +;; asm "THROWARGIF"; + +cell get_data() asm "c4 PUSH"; +() set_data(cell c) impure asm "c4 POP"; +() accept_message() impure asm "ACCEPT"; + +slice begin_parse(cell c) asm "CTOS"; +() end_parse(slice s) impure asm "ENDS"; +(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF"; +(slice, int) zload_int(slice s, int len) asm(s len -> 1 0) "LDIX"; +(slice, int) zload_uint(slice s, int len) asm( -> 1 0) "LDUX"; +int zpreload_int(slice s, int len) asm "PLDIX"; +int zpreload_uint(slice s, int len) asm "PLDUX"; +(slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX"; +slice preload_bits(slice s, int len) asm "PLDSLICEX"; +cell set_idict_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; +builder begin_cell() asm "NEWC"; +builder store_ref(builder b, cell c) asm(c b) "STREF"; +builder zstore_uint(builder b, int x, int len) asm(x b len) "STUX"; +builder zstore_int(builder b, int x, int len) asm(x b len) "STIX"; +cell end_cell(builder b) asm "ENDC"; + +;; Simple configuration smart contract + +() recv_internal(cell in_msg) impure { + ;; do nothing for internal messages +} + +() recv_external(cell in_msg) impure { + var (cs0, signature) = load_bits(begin_parse(in_msg), 512); + var (cs, msg_seqno) = zload_uint(cs0, 32); + (cs, var valid_until) = zload_uint(cs, 32); + throw_if(35, valid_until < now()); + var (cs2, cfg_dict) = load_ref(begin_parse(get_data())); + (cs2, var stored_seqno) = zload_uint(cs2, 32); + (cs2, var public_key) = zload_uint(cs2, 256); + end_parse(cs2); + throw_unless(33, msg_seqno == stored_seqno); + throw_unless(34, check_signature(slice_hash(cs0), signature, public_key)); + accept_message(); + (cs, var param_index) = zload_uint(cs, 32); + (cs, var param_value) = load_ref(cs); + end_parse(cs); +;; cfg_dict = set_idict_ref(cfg_dict, 32, param_index, param_value); +;; var cb = begin_cell(); +;; cb = store_ref(cb, cfg_dict); + var cb = store_ref(begin_cell(), set_idict_ref(cfg_dict, 32, param_index, param_value)); + cb = zstore_uint(cb, stored_seqno + 1, 32); + cb = zstore_uint(cb, public_key, 256); + set_data(end_cell(cb)); +} diff --git a/ton-test-liteclient-full/lite-client/crypto/func/b2_1.fc b/ton-test-liteclient-full/lite-client/crypto/func/b2_1.fc new file mode 100644 index 0000000..1ff79d0 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/b2_1.fc @@ -0,0 +1,62 @@ +int now() asm "NOW"; + +int cell_hash(cell c) +asm "HASHCU"; + +int slice_hash(slice s) +asm "HASHSU"; + +int check_signature(int hash, slice signature, int public_key) +asm "CHKSIGNU"; + +;; () throw_if(int excno, int cond) impure +;; asm "THROWARGIF"; + +cell get_data() asm "c4 PUSH"; +() set_data(cell c) impure asm "c4 POP"; +() accept_message() impure asm "ACCEPT"; + +slice begin_parse(cell c) asm "CTOS"; +() end_parse(slice s) impure asm "ENDS"; +(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF"; +;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX"; +;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX"; +;; int preload_int(slice s, int len) asm "PLDIX"; +;; int preload_uint(slice s, int len) asm "PLDUX"; +(slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX"; +slice preload_bits(slice s, int len) asm "PLDSLICEX"; +cell set_idict_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; +(cell, ()) ~set_idict_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; +builder begin_cell() asm "NEWC"; +builder store_ref(builder b, cell c) asm(c b) "STREF"; +;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX"; +;; builder store_int(builder b, int x, int len) asm(x b len) "STIX"; +cell end_cell(builder b) asm "ENDC"; + +;; Simple configuration smart contract + +() recv_internal(cell in_msg) impure { + ;; do nothing for internal messages +} + +() recv_external(cell in_msg) impure { + var cs = begin_parse(in_msg); + var signature = cs~load_bits(512); + var cs0 = cs; + int msg_seqno = cs~load_uint(32); + var valid_until = cs~load_uint(32); + throw_if(35, valid_until < now()); + var cs2 = begin_parse(get_data()); + var cfg_dict = cs2~load_ref(); + var stored_seqno = cs2~load_uint(32); + var public_key = cs2~load_uint(256); + cs2.end_parse(); + throw_unless(33, msg_seqno == stored_seqno); + throw_unless(34, check_signature(slice_hash(cs0), signature, public_key)); + accept_message(); + var param_index = cs~load_uint(32); + var param_value = cs~load_ref(); + cs.end_parse(); + cfg_dict~set_idict_ref(32, param_index, param_value); + set_data(begin_cell().store_ref(cfg_dict).store_uint(stored_seqno + 1, 32).store_uint(public_key, 256).end_cell()); +} diff --git a/ton-test-liteclient-full/lite-client/crypto/func/b2_2.fc b/ton-test-liteclient-full/lite-client/crypto/func/b2_2.fc new file mode 100644 index 0000000..d137ff6 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/b2_2.fc @@ -0,0 +1,58 @@ +int now() asm "NOW"; + +int cell_hash(cell c) +asm "HASHCU"; + +int slice_hash(slice s) +asm "HASHSU"; + +int check_signature(int hash, slice signature, int public_key) +asm "CHKSIGNU"; + +;; () throw_if(int excno, int cond) impure +;; asm "THROWARGIF"; + +cell get_data() asm "c4 PUSH"; +() set_data(cell c) impure asm "c4 POP"; +() accept_message() impure asm "ACCEPT"; + +slice begin_parse(cell c) asm "CTOS"; +() end_parse(slice s) impure asm "ENDS"; +(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF"; +;; (slice, int) load_int(slice s, int len) asm(s len -> 1 0) "LDIX"; +;; (slice, int) load_uint(slice s, int len) asm( -> 1 0) "LDUX"; +;; int .preload_int(slice s, int len) asm "PLDIX"; +;; int .preload_uint(slice s, int len) asm "PLDUX"; +(slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX"; +;; slice .preload_bits(slice s, int len) asm "PLDSLICEX"; +cell set_idict_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; +builder begin_cell() asm "NEWC"; +builder store_ref(builder b, cell c) asm(c b) "STREF"; +;;builder .store_uint(builder b, int x, int len) asm(x b len) "STUX"; +;;builder .store_int(builder b, int x, int len) asm(x b len) "STIX"; +cell .end_cell(builder b) asm "ENDC"; + +;; Simple configuration smart contract + +() recv_internal(cell in_msg) impure { + ;; do nothing for internal messages +} + +() recv_external(cell in_msg) impure { + var (cs0, signature) = load_bits(begin_parse(in_msg), 512); + var (cs, msg_seqno) = load_uint(cs0, 32); + (cs, var valid_until) = load_uint(cs, 32); + throw_if(35, valid_until < now()); + var (cs2, cfg_dict) = load_ref(begin_parse(get_data())); + (cs2, var stored_seqno) = load_uint(cs2, 32); + (cs2, var public_key) = load_uint(cs2, 256); + end_parse(cs2); + throw_unless(33, msg_seqno == stored_seqno); + throw_unless(34, check_signature(slice_hash(cs0), signature, public_key)); + accept_message(); + (cs, var param_index) = load_uint(cs, 32); + (cs, var param_value) = load_ref(cs); + end_parse(cs); + set_data(begin_cell().store_ref(cfg_dict.set_idict_ref(32, param_index, param_value)) + .store_uint(stored_seqno + 1, 32).store_uint(public_key, 256).end_cell()); +} diff --git a/ton-test-liteclient-full/lite-client/crypto/func/builtins.cpp b/ton-test-liteclient-full/lite-client/crypto/func/builtins.cpp new file mode 100644 index 0000000..f603049 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/builtins.cpp @@ -0,0 +1,824 @@ +#include "func.h" + +namespace funC { +using namespace std::literals::string_literals; + +/* + * + * SYMBOL VALUES + * + */ + +int glob_func_cnt, undef_func_cnt; +std::vector glob_func; + +SymDef* predefine_builtin_func(std::string name, TypeExpr* func_type) { + sym_idx_t name_idx = sym::symbols.lookup(name, 1); + if (sym::symbols.is_keyword(name_idx)) { + std::cerr << "fatal: global function `" << name << "` already defined as a keyword" << std::endl; + } + SymDef* def = sym::define_global_symbol(name_idx, true); + if (!def) { + std::cerr << "fatal: global function `" << name << "` already defined" << std::endl; + std::exit(1); + } + return def; +} + +void define_builtin_func(std::string name, TypeExpr* func_type, const AsmOp& macro, bool impure = false) { + SymDef* def = predefine_builtin_func(name, func_type); + def->value = new SymValAsmFunc{func_type, macro, impure}; +} + +void define_builtin_func(std::string name, TypeExpr* func_type, const simple_compile_func_t func, bool impure = false) { + SymDef* def = predefine_builtin_func(name, func_type); + def->value = new SymValAsmFunc{func_type, func, impure}; +} + +void define_builtin_func(std::string name, TypeExpr* func_type, const simple_compile_func_t func, + std::initializer_list arg_order, std::initializer_list ret_order = {}, + bool impure = false) { + SymDef* def = predefine_builtin_func(name, func_type); + def->value = new SymValAsmFunc{func_type, func, arg_order, ret_order, impure}; +} + +/* + * + * DEFINE BUILT-IN FUNCTIONS + * + */ + +int emulate_negate(int a) { + int f = VarDescr::_Pos | VarDescr::_Neg; + if ((a & f) && (~a & f)) { + a ^= f; + } + f = VarDescr::_Bit | VarDescr::_Bool; + if ((a & f) && (~a & f)) { + a ^= f; + } + return a; +} + +int emulate_add(int a, int b) { + if (b & VarDescr::_Zero) { + return a; + } else if (a & VarDescr::_Zero) { + return b; + } + int u = a & b, v = a | b; + int r = VarDescr::_Int; + int t = u & (VarDescr::_Pos | VarDescr::_Neg); + if (v & VarDescr::_Nan) { + return r | VarDescr::_Nan; + } + // non-quiet addition always returns finite results! + r |= t | VarDescr::_Finite; + if (t) { + r |= v & VarDescr::_NonZero; + } + r |= v & VarDescr::_Nan; + if (u & (VarDescr::_Odd | VarDescr::_Even)) { + r |= VarDescr::_Even; + } else if (!(~v & (VarDescr::_Odd | VarDescr::_Even))) { + r |= VarDescr::_Odd | VarDescr::_NonZero; + } + return r; +} + +int emulate_sub(int a, int b) { + return emulate_add(a, emulate_negate(b)); +} + +int emulate_mul(int a, int b) { + if ((b & (VarDescr::_NonZero | VarDescr::_Bit)) == (VarDescr::_NonZero | VarDescr::_Bit)) { + return a; + } else if ((a & (VarDescr::_NonZero | VarDescr::_Bit)) == (VarDescr::_NonZero | VarDescr::_Bit)) { + return b; + } + int u = a & b, v = a | b; + int r = VarDescr::_Int; + if (v & VarDescr::_Nan) { + return r | VarDescr::_Nan; + } + // non-quiet multiplication always yields finite results, if any + r |= VarDescr::_Finite; + if (v & VarDescr::_Zero) { + // non-quiet multiplication + // the result is zero, if any result at all + return VarDescr::ConstZero; + } + if (u & (VarDescr::_Pos | VarDescr::_Neg)) { + r |= VarDescr::_Pos; + } else if (!(~v & (VarDescr::_Pos | VarDescr::_Neg))) { + r |= VarDescr::_Neg; + } + if (u & (VarDescr::_Bit | VarDescr::_Bool)) { + r |= VarDescr::_Bit; + } else if (!(~v & (VarDescr::_Bit | VarDescr::_Bool))) { + r |= VarDescr::_Bool; + } + r |= v & VarDescr::_Even; + r |= u & (VarDescr::_Odd | VarDescr::_NonZero); + return r; +} + +int emulate_lshift(int a, int b) { + if (((a | b) & VarDescr::_Nan) || !(~b & (VarDescr::_Neg | VarDescr::_NonZero))) { + return VarDescr::_Int | VarDescr::_Nan; + } + if (b & VarDescr::_Zero) { + return a; + } + int t = ((b & VarDescr::_NonZero) ? VarDescr::_Even : 0); + t |= b & VarDescr::_Finite; + return emulate_mul(a, VarDescr::_Int | VarDescr::_Pos | VarDescr::_NonZero | VarDescr::_Even | t); +} + +int emulate_div(int a, int b) { + if ((b & (VarDescr::_NonZero | VarDescr::_Bit)) == (VarDescr::_NonZero | VarDescr::_Bit)) { + return a; + } else if ((b & (VarDescr::_NonZero | VarDescr::_Bool)) == (VarDescr::_NonZero | VarDescr::_Bool)) { + return emulate_negate(b); + } + if (b & VarDescr::_Zero) { + return VarDescr::_Int | VarDescr::_Nan; + } + int u = a & b, v = a | b; + int r = VarDescr::_Int; + if (v & VarDescr::_Nan) { + return r | VarDescr::_Nan; + } + // non-quiet division always yields finite results, if any + r |= VarDescr::_Finite; + if (a & VarDescr::_Zero) { + // non-quiet division + // the result is zero, if any result at all + return VarDescr::ConstZero; + } + if (u & (VarDescr::_Pos | VarDescr::_Neg)) { + r |= VarDescr::_Pos; + } else if (!(~v & (VarDescr::_Pos | VarDescr::_Neg))) { + r |= VarDescr::_Neg; + } + if (u & (VarDescr::_Bit | VarDescr::_Bool)) { + r |= VarDescr::_Bit; + } else if (!(~v & (VarDescr::_Bit | VarDescr::_Bool))) { + r |= VarDescr::_Bool; + } + return r; +} + +int emulate_rshift(int a, int b) { + if (((a | b) & VarDescr::_Nan) || !(~b & (VarDescr::_Neg | VarDescr::_NonZero))) { + return VarDescr::_Int | VarDescr::_Nan; + } + if (b & VarDescr::_Zero) { + return a; + } + int t = ((b & VarDescr::_NonZero) ? VarDescr::_Even : 0); + t |= b & VarDescr::_Finite; + return emulate_div(a, VarDescr::_Int | VarDescr::_Pos | VarDescr::_NonZero | VarDescr::_Even | t); +} + +int emulate_mod(int a, int b, int round_mode = -1) { + if ((b & (VarDescr::_NonZero | VarDescr::_Bit)) == (VarDescr::_NonZero | VarDescr::_Bit)) { + return VarDescr::ConstZero; + } else if ((b & (VarDescr::_NonZero | VarDescr::_Bool)) == (VarDescr::_NonZero | VarDescr::_Bool)) { + return VarDescr::ConstZero; + } + if (b & VarDescr::_Zero) { + return VarDescr::_Int | VarDescr::_Nan; + } + int r = VarDescr::_Int; + if ((a | b) & VarDescr::_Nan) { + return r | VarDescr::_Nan; + } + // non-quiet division always yields finite results, if any + r |= VarDescr::_Finite; + if (a & VarDescr::_Zero) { + // non-quiet division + // the result is zero, if any result at all + return VarDescr::ConstZero; + } + if (round_mode < 0) { + r |= b & (VarDescr::_Pos | VarDescr::_Neg); + } else if (round_mode > 0) { + r |= emulate_negate(b) & (VarDescr::_Pos | VarDescr::_Neg); + } + if (a & (VarDescr::_Bit | VarDescr::_Bool)) { + if (r & VarDescr::_Pos) { + r |= VarDescr::_Bit; + } + if (r & VarDescr::_Neg) { + r |= VarDescr::_Bool; + } + } + if (b & VarDescr::_Even) { + r |= a & (VarDescr::_Even | VarDescr::_Odd); + } + return r; +} + +bool VarDescr::always_less(const VarDescr& other) const { + if (is_int_const() && other.is_int_const()) { + return int_const < other.int_const; + } + return (always_nonpos() && other.always_pos()) || (always_neg() && other.always_nonneg()); +} + +bool VarDescr::always_leq(const VarDescr& other) const { + if (is_int_const() && other.is_int_const()) { + return int_const <= other.int_const; + } + return always_nonpos() && other.always_nonneg(); +} + +bool VarDescr::always_greater(const VarDescr& other) const { + return other.always_less(*this); +} + +bool VarDescr::always_geq(const VarDescr& other) const { + return other.always_leq(*this); +} + +bool VarDescr::always_equal(const VarDescr& other) const { + return is_int_const() && other.is_int_const() && *int_const == *other.int_const; +} + +bool VarDescr::always_neq(const VarDescr& other) const { + if (is_int_const() && other.is_int_const()) { + return *int_const != *other.int_const; + } + return always_greater(other) || always_less(other) || (always_even() && other.always_odd()) || + (always_odd() && other.always_even()); +} + +AsmOp exec_op(std::string op) { + return AsmOp::Custom(op); +} + +AsmOp exec_op(std::string op, int args, int retv = 1) { + return AsmOp::Custom(op, args, retv); +} + +AsmOp exec_arg_op(std::string op, long long arg) { + std::ostringstream os; + os << arg << ' ' << op; + return AsmOp::Custom(os.str()); +} + +AsmOp exec_arg_op(std::string op, long long arg, int args, int retv) { + std::ostringstream os; + os << arg << ' ' << op; + return AsmOp::Custom(os.str(), args, retv); +} + +AsmOp exec_arg_op(std::string op, td::RefInt256 arg) { + std::ostringstream os; + os << arg << ' ' << op; + return AsmOp::Custom(os.str()); +} + +AsmOp exec_arg_op(std::string op, td::RefInt256 arg, int args, int retv) { + std::ostringstream os; + os << arg << ' ' << op; + return AsmOp::Custom(os.str(), args, retv); +} + +AsmOp push_const(td::RefInt256 x) { + return AsmOp::IntConst(std::move(x)); +} + +AsmOp compile_add(std::vector& res, std::vector& args) { + assert(res.size() == 1 && args.size() == 2); + VarDescr &r = res[0], &x = args[0], &y = args[1]; + if (x.is_int_const() && y.is_int_const()) { + r.set_const(x.int_const + y.int_const); + x.unused(); + y.unused(); + return push_const(r.int_const); + } + r.val = emulate_add(x.val, y.val); + if (y.is_int_const() && y.int_const->signed_fits_bits(8)) { + y.unused(); + if (y.always_zero()) { + return AsmOp::Nop(); + } + if (*y.int_const == 1) { + return exec_op("INC", 1); + } + if (*y.int_const == -1) { + return exec_op("DEC", 1); + } + return exec_arg_op("ADDCONST", y.int_const, 1); + } + if (x.is_int_const() && x.int_const->signed_fits_bits(8)) { + x.unused(); + if (x.always_zero()) { + return AsmOp::Nop(); + } + if (*x.int_const == 1) { + return exec_op("INC", 1); + } + if (*x.int_const == -1) { + return exec_op("DEC", 1); + } + return exec_arg_op("ADDCONST", x.int_const, 1); + } + return exec_op("ADD", 2); +} + +AsmOp compile_sub(std::vector& res, std::vector& args) { + assert(res.size() == 1 && args.size() == 2); + VarDescr &r = res[0], &x = args[0], &y = args[1]; + if (x.is_int_const() && y.is_int_const()) { + r.set_const(x.int_const - y.int_const); + x.unused(); + y.unused(); + return push_const(r.int_const); + } + r.val = emulate_sub(x.val, y.val); + if (y.is_int_const() && (-y.int_const)->signed_fits_bits(8)) { + y.unused(); + if (y.always_zero()) { + return {}; + } + if (*y.int_const == 1) { + return exec_op("DEC", 1); + } + if (*y.int_const == -1) { + return exec_op("INC", 1); + } + return exec_arg_op("ADDCONST", -y.int_const, 1); + } + if (x.always_zero()) { + x.unused(); + return exec_op("NEGATE", 1); + } + return exec_op("SUB", 2); +} + +AsmOp compile_negate(std::vector& res, std::vector& args) { + assert(res.size() == 1 && args.size() == 1); + VarDescr &r = res[0], &x = args[0]; + if (x.is_int_const()) { + r.set_const(-x.int_const); + x.unused(); + return push_const(r.int_const); + } + r.val = emulate_negate(x.val); + return exec_op("NEGATE", 1); +} + +AsmOp compile_mul(std::vector& res, std::vector& args) { + assert(res.size() == 1 && args.size() == 2); + VarDescr &r = res[0], &x = args[0], &y = args[1]; + if (x.is_int_const() && y.is_int_const()) { + r.set_const(x.int_const * y.int_const); + x.unused(); + y.unused(); + return push_const(r.int_const); + } + r.val = emulate_mul(x.val, y.val); + if (y.is_int_const()) { + int k = is_pos_pow2(y.int_const); + if (y.int_const->signed_fits_bits(8) && k < 0) { + y.unused(); + if (y.always_zero() && x.always_finite()) { + // dubious optimization: NaN * 0 = ? + r.set_const(y.int_const); + return push_const(r.int_const); + } + if (*y.int_const == 1 && x.always_finite()) { + return AsmOp::Nop(); + } + if (*y.int_const == -1) { + return exec_op("NEGATE", 1); + } + return exec_arg_op("MULCONST", y.int_const, 1); + } + if (k >= 0) { + y.unused(); + return exec_arg_op("LSHIFT#", k, 1); + } + } + if (x.is_int_const()) { + int k = is_pos_pow2(x.int_const); + if (x.int_const->signed_fits_bits(8) && k < 0) { + x.unused(); + if (x.always_zero() && y.always_finite()) { + // dubious optimization: NaN * 0 = ? + r.set_const(x.int_const); + return push_const(r.int_const); + } + if (*x.int_const == 1 && y.always_finite()) { + return AsmOp::Nop(); + } + if (*x.int_const == -1) { + return exec_op("NEGATE", 1); + } + return exec_arg_op("MULCONST", x.int_const, 1); + } + if (k >= 0) { + x.unused(); + return exec_arg_op("LSHIFT#", k, 1); + } + } + return exec_op("MUL", 2); +} + +AsmOp compile_lshift(std::vector& res, std::vector& args) { + assert(res.size() == 1 && args.size() == 2); + VarDescr &r = res[0], &x = args[0], &y = args[1]; + if (y.is_int_const()) { + auto yv = y.int_const->to_long(); + if (yv < 0 || yv > 256) { + r.set_const_nan(); + x.unused(); + y.unused(); + return push_const(r.int_const); + } else if (x.is_int_const()) { + r.set_const(x.int_const << (int)yv); + x.unused(); + y.unused(); + return push_const(r.int_const); + } + } + r.val = emulate_lshift(x.val, y.val); + if (y.is_int_const()) { + int k = (int)(y.int_const->to_long()); + if (!k /* && x.always_finite() */) { + // dubious optimization: what if x=NaN ? + y.unused(); + return AsmOp::Nop(); + } + y.unused(); + return exec_arg_op("LSHIFT#", k, 1); + } + if (x.is_int_const()) { + auto xv = x.int_const->to_long(); + if (xv == 1) { + x.unused(); + return exec_op("POW2", 1); + } + if (xv == -1) { + x.unused(); + return exec_op("NEGPOW2", 1); + } + } + return exec_op("LSHIFT", 2); +} + +AsmOp compile_rshift(std::vector& res, std::vector& args, int round_mode) { + assert(res.size() == 1 && args.size() == 2); + VarDescr &r = res[0], &x = args[0], &y = args[1]; + if (y.is_int_const()) { + auto yv = y.int_const->to_long(); + if (yv < 0 || yv > 256) { + r.set_const_nan(); + x.unused(); + y.unused(); + return push_const(r.int_const); + } else if (x.is_int_const()) { + r.set_const(td::rshift(x.int_const, (int)yv, round_mode)); + x.unused(); + y.unused(); + return push_const(r.int_const); + } + } + r.val = emulate_rshift(x.val, y.val); + std::string rshift = (round_mode < 0 ? "RSHIFT" : (round_mode ? "RSHIFTC" : "RSHIFTR")); + if (y.is_int_const()) { + int k = (int)(y.int_const->to_long()); + if (!k /* && x.always_finite() */) { + // dubious optimization: what if x=NaN ? + y.unused(); + return AsmOp::Nop(); + } + y.unused(); + return exec_arg_op(rshift + "#", k, 1); + } + return exec_op(rshift, 2); +} + +AsmOp compile_div(std::vector& res, std::vector& args, int round_mode) { + assert(res.size() == 1 && args.size() == 2); + VarDescr &r = res[0], &x = args[0], &y = args[1]; + if (x.is_int_const() && y.is_int_const()) { + r.set_const(div(x.int_const, y.int_const, round_mode)); + x.unused(); + y.unused(); + return push_const(r.int_const); + } + r.val = emulate_div(x.val, y.val); + if (y.is_int_const()) { + if (*y.int_const == 0) { + x.unused(); + y.unused(); + r.set_const(div(y.int_const, y.int_const)); + return push_const(r.int_const); + } + if (*y.int_const == 1 && x.always_finite()) { + y.unused(); + return AsmOp::Nop(); + } + if (*y.int_const == -1) { + y.unused(); + return exec_op("NEGATE", 1); + } + int k = is_pos_pow2(y.int_const); + if (k > 0) { + y.unused(); + std::string op = "RSHIFT"; + if (round_mode >= 0) { + op += (round_mode > 0 ? 'C' : 'R'); + } + return exec_arg_op(op + '#', k, 1); + } + } + std::string op = "DIV"; + if (round_mode >= 0) { + op += (round_mode > 0 ? 'C' : 'R'); + } + return exec_op(op, 2); +} + +AsmOp compile_mod(std::vector& res, std::vector& args, int round_mode) { + assert(res.size() == 1 && args.size() == 2); + VarDescr &r = res[0], &x = args[0], &y = args[1]; + if (x.is_int_const() && y.is_int_const()) { + r.set_const(mod(x.int_const, y.int_const, round_mode)); + x.unused(); + y.unused(); + return push_const(r.int_const); + } + r.val = emulate_mod(x.val, y.val); + if (y.is_int_const()) { + if (*y.int_const == 0) { + x.unused(); + y.unused(); + r.set_const(mod(y.int_const, y.int_const)); + return push_const(r.int_const); + } + if ((*y.int_const == 1 || *y.int_const == -1) && x.always_finite()) { + x.unused(); + y.unused(); + r.set_const(td::RefInt256{true, 0}); + return push_const(r.int_const); + } + int k = is_pos_pow2(y.int_const); + if (k > 0) { + y.unused(); + std::string op = "MODPOW2"; + if (round_mode >= 0) { + op += (round_mode > 0 ? 'C' : 'R'); + } + return exec_arg_op(op + '#', k, 1); + } + } + std::string op = "MOD"; + if (round_mode >= 0) { + op += (round_mode > 0 ? 'C' : 'R'); + } + return exec_op(op, 2); +} + +int compute_compare(td::RefInt256 x, td::RefInt256 y, int mode) { + int s = td::cmp(x, y); + if (mode == 7) { + return s; + } else { + return (mode >> (1 - s)) & 1; + } +} + +// return value: +// 4 -> constant 1 +// 2 -> constant 0 +// 1 -> constant -1 +// 3 -> 0 or -1 +int compute_compare(const VarDescr& x, const VarDescr& y, int mode) { + switch (mode) { + case 1: // > + return x.always_greater(y) ? 1 : (x.always_leq(y) ? 2 : 3); + case 2: // = + return x.always_equal(y) ? 1 : (x.always_neq(y) ? 2 : 3); + case 3: // >= + return x.always_geq(y) ? 1 : (x.always_less(y) ? 2 : 3); + case 4: // < + return x.always_less(y) ? 1 : (x.always_geq(y) ? 2 : 3); + case 5: // <> + return x.always_neq(y) ? 1 : (x.always_equal(y) ? 2 : 3); + case 6: // >= + return x.always_geq(y) ? 1 : (x.always_less(y) ? 2 : 3); + case 7: // <=> + return x.always_less(y) + ? 1 + : (x.always_equal(y) + ? 2 + : (x.always_greater(y) + ? 4 + : (x.always_leq(y) ? 3 : (x.always_geq(y) ? 6 : (x.always_neq(y) ? 5 : 7))))); + default: + return 7; + } +} + +AsmOp compile_cmp_int(std::vector& res, std::vector& args, int mode) { + assert(mode >= 1 && mode <= 7); + assert(res.size() == 1 && args.size() == 2); + VarDescr &r = res[0], &x = args[0], &y = args[1]; + if (x.is_int_const() && y.is_int_const()) { + r.set_const(compute_compare(x.int_const, y.int_const, mode)); + x.unused(); + y.unused(); + return push_const(r.int_const); + } + int v = compute_compare(x, y, mode); + assert(v); + if (!(v & (v - 1))) { + r.set_const(v - (v >> 2) - 2); + x.unused(); + y.unused(); + return push_const(r.int_const); + } + r.val = ~0; + if (v & 1) { + r.val &= VarDescr::ConstTrue; + } + if (v & 2) { + r.val &= VarDescr::ConstZero; + } + if (v & 4) { + r.val &= VarDescr::ConstOne; + } + static const char* cmp_int_names[] = {"", "GTINT", "EQINT", "GTINT", "LESSINT", "NEQINT", "LESSINT"}; + static const char* cmp_names[] = {"", "GREATER", "EQUAL", "GEQ", "LESS", "NEQ", "LEQ", "CMP"}; + static int cmp_int_delta[] = {0, 0, 0, -1, 0, 0, 1}; + if (mode != 7) { + if (y.is_int_const()) { + y.unused(); + return exec_arg_op(cmp_int_names[mode], y.int_const + cmp_int_delta[mode], 1); + } + if (x.is_int_const()) { + x.unused(); + mode = ((mode & 4) >> 2) | (mode & 2) | ((mode & 1) << 2); + return exec_arg_op(cmp_int_names[mode], x.int_const + cmp_int_delta[mode], 1); + } + } + return exec_op(cmp_names[mode], 2); +} + +AsmOp compile_throw(std::vector& res, std::vector& args) { + assert(res.empty() && args.size() == 1); + VarDescr& x = args[0]; + if (x.is_int_const() && x.int_const->unsigned_fits_bits(11)) { + x.unused(); + return exec_arg_op("THROW", x.int_const, 0, 0); + } else { + return exec_op("THROWANY", 1, 0); + } +} + +AsmOp compile_cond_throw(std::vector& res, std::vector& args, bool mode) { + assert(res.empty() && args.size() == 2); + VarDescr &x = args[0], &y = args[1]; + std::string suff = (mode ? "IF" : "IFNOT"); + bool skip_cond = false; + if (y.always_true() || y.always_false()) { + y.unused(); + skip_cond = true; + if (y.always_true() != mode) { + x.unused(); + return AsmOp::Nop(); + } + } + if (x.is_int_const() && x.int_const->unsigned_fits_bits(11)) { + x.unused(); + return skip_cond ? exec_arg_op("THROW", x.int_const, 0, 0) : exec_arg_op("THROW"s + suff, x.int_const, 1, 0); + } else { + return skip_cond ? exec_op("THROWANY", 1, 0) : exec_arg_op("THROWANY"s + suff, 2, 0); + } +} + +AsmOp compile_bool_const(std::vector& res, std::vector& args, bool val) { + assert(res.size() == 1 && args.empty()); + VarDescr& r = res[0]; + r.set_const(val ? -1 : 0); + return AsmOp::Const(val ? "TRUE" : "FALSE"); +} + +// (slice, int) load_int(slice s, int len) asm(s len -> 1 0) "LDIX"; +// (slice, int) load_uint(slice s, int len) asm( -> 1 0) "LDUX"; +// int preload_int(slice s, int len) asm "PLDIX"; +// int preload_uint(slice s, int len) asm "PLDUX"; +AsmOp compile_fetch_int(std::vector& res, std::vector& args, bool fetch, bool sgnd) { + assert(args.size() == 2 && res.size() == 1 + (unsigned)fetch); + auto &y = args[1], &r = res.back(); + r.val = (sgnd ? VarDescr::FiniteInt : VarDescr::FiniteUInt); + int v = -1; + if (y.is_int_const() && y.int_const >= 0 && y.int_const <= 256) { + v = (int)y.int_const->to_long(); + if (!v) { + r.val = VarDescr::ConstZero; + } + if (v == 1) { + r.val = (sgnd ? VarDescr::ValBool : VarDescr::ValBit); + } + if (v > 0) { + y.unused(); + return exec_arg_op((fetch ? "LD"s : "PLD"s) + (sgnd ? 'I' : 'U'), v, 1, 1 + (unsigned)fetch); + } + } + return exec_op((fetch ? "LD"s : "PLD"s) + (sgnd ? "IX" : "UX"), 2, 1 + (unsigned)fetch); +} + +// builder store_uint(builder b, int x, int len) asm(x b len) "STUX"; +// builder store_int(builder b, int x, int len) asm(x b len) "STIX"; +AsmOp compile_store_int(std::vector& res, std::vector& args, bool sgnd) { + assert(args.size() == 3 && res.size() == 1); + auto& z = args[2]; + if (z.is_int_const() && z.int_const > 0 && z.int_const <= 256) { + z.unused(); + return exec_arg_op("ST"s + (sgnd ? 'I' : 'U'), z.int_const, 2, 1); + } + return exec_op("ST"s + (sgnd ? "IX" : "UX"), 3, 1); +} + +void define_builtins() { + using namespace std::placeholders; + auto Unit = TypeExpr::new_unit(); + auto Int = TypeExpr::new_atomic(_Int); + auto Slice = TypeExpr::new_atomic(_Slice); + auto Builder = TypeExpr::new_atomic(_Builder); + // auto Null = TypeExpr::new_atomic(_Null); + auto Int2 = TypeExpr::new_tensor({Int, Int}); + auto Int3 = TypeExpr::new_tensor({Int, Int, Int}); + auto arith_bin_op = TypeExpr::new_map(Int2, Int); + auto arith_un_op = TypeExpr::new_map(Int, Int); + auto impure_bin_op = TypeExpr::new_map(Int2, Unit); + auto impure_un_op = TypeExpr::new_map(Int, Unit); + auto fetch_int_op = TypeExpr::new_map(TypeExpr::new_tensor({Slice, Int}), TypeExpr::new_tensor({Slice, Int})); + auto prefetch_int_op = TypeExpr::new_map(TypeExpr::new_tensor({Slice, Int}), Int); + auto store_int_op = TypeExpr::new_map(TypeExpr::new_tensor({Builder, Int, Int}), Builder); + //auto arith_null_op = TypeExpr::new_map(TypeExpr::new_unit(), Int); + define_builtin_func("_+_", arith_bin_op, compile_add); + define_builtin_func("_-_", arith_bin_op, compile_sub); + define_builtin_func("-_", arith_un_op, compile_negate); + define_builtin_func("_*_", arith_bin_op, compile_mul); + define_builtin_func("_/_", arith_bin_op, std::bind(compile_div, _1, _2, -1)); + define_builtin_func("_/~_", arith_bin_op, std::bind(compile_div, _1, _2, 0)); + define_builtin_func("_/^_", arith_bin_op, std::bind(compile_div, _1, _2, 1)); + define_builtin_func("_%_", arith_bin_op, std::bind(compile_mod, _1, _2, -1)); + define_builtin_func("_%~_", arith_bin_op, std::bind(compile_mod, _1, _2, 0)); + define_builtin_func("_%^_", arith_bin_op, std::bind(compile_mod, _1, _2, -1)); + define_builtin_func("_/%_", TypeExpr::new_map(Int2, Int2), AsmOp::Custom("DIVMOD", 2, 2)); + define_builtin_func("_<<_", arith_bin_op, compile_lshift); + define_builtin_func("_>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, -1)); + define_builtin_func("_>>~_", arith_bin_op, std::bind(compile_rshift, _1, _2, 0)); + define_builtin_func("_>>^_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1)); + define_builtin_func("_&_", arith_bin_op, AsmOp::Custom("AND", 2)); + define_builtin_func("_|_", arith_bin_op, AsmOp::Custom("OR", 2)); + define_builtin_func("_^_", arith_bin_op, AsmOp::Custom("XOR", 2)); + define_builtin_func("^_+=_", arith_bin_op, compile_add); + define_builtin_func("^_-=_", arith_bin_op, compile_sub); + define_builtin_func("^_*=_", arith_bin_op, compile_mul); + define_builtin_func("^_/=_", arith_bin_op, std::bind(compile_div, _1, _2, -1)); + define_builtin_func("^_/~=_", arith_bin_op, std::bind(compile_div, _1, _2, 0)); + define_builtin_func("^_/^=_", arith_bin_op, std::bind(compile_div, _1, _2, 1)); + define_builtin_func("^_%=_", arith_bin_op, std::bind(compile_mod, _1, _2, -1)); + define_builtin_func("^_%~=_", arith_bin_op, std::bind(compile_mod, _1, _2, 0)); + define_builtin_func("^_%^=_", arith_bin_op, std::bind(compile_mod, _1, _2, 1)); + define_builtin_func("^_<<=_", arith_bin_op, compile_lshift); + define_builtin_func("^_>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, -1)); + define_builtin_func("^_>>~=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 0)); + define_builtin_func("^_>>^=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1)); + define_builtin_func("^_&=_", arith_bin_op, AsmOp::Custom("AND", 2)); + define_builtin_func("^_|=_", arith_bin_op, AsmOp::Custom("OR", 2)); + define_builtin_func("^_^=_", arith_bin_op, AsmOp::Custom("XOR", 2)); + define_builtin_func("muldivr", TypeExpr::new_map(Int3, Int), AsmOp::Custom("MULDIVR", 3)); + define_builtin_func("muldiv", TypeExpr::new_map(Int3, Int), AsmOp::Custom("MULDIV", 3)); + define_builtin_func("muldivmod", TypeExpr::new_map(Int3, Int2), AsmOp::Custom("MULDIVMOD", 3, 2)); + define_builtin_func("_==_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 2)); + define_builtin_func("_!=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 5)); + define_builtin_func("_<_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 4)); + define_builtin_func("_>_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 1)); + define_builtin_func("_<=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 6)); + define_builtin_func("_>=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 3)); + define_builtin_func("_<=>_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 7)); + define_builtin_func("true", Int, /* AsmOp::Const("TRUE") */ std::bind(compile_bool_const, _1, _2, true)); + define_builtin_func("false", Int, /* AsmOp::Const("FALSE") */ std::bind(compile_bool_const, _1, _2, false)); + // define_builtin_func("null", Null, AsmOp::Const("PUSHNULL")); + define_builtin_func("throw", impure_un_op, compile_throw, true); + define_builtin_func("throw_if", impure_bin_op, std::bind(compile_cond_throw, _1, _2, true), true); + define_builtin_func("throw_unless", impure_bin_op, std::bind(compile_cond_throw, _1, _2, false), true); + define_builtin_func("load_int", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, true), {}, {1, 0}); + define_builtin_func("load_uint", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, false), {}, {1, 0}); + define_builtin_func("preload_int", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, true)); + define_builtin_func("preload_uint", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, false)); + define_builtin_func("store_int", store_int_op, std::bind(compile_store_int, _1, _2, true), {1, 0, 2}); + define_builtin_func("store_uint", store_int_op, std::bind(compile_store_int, _1, _2, false), {1, 0, 2}); +} + +} // namespace funC diff --git a/ton-test-liteclient-full/lite-client/crypto/func/codegen.cpp b/ton-test-liteclient-full/lite-client/crypto/func/codegen.cpp new file mode 100644 index 0000000..9816d15 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/codegen.cpp @@ -0,0 +1,669 @@ +#include "func.h" + +namespace funC { + +/* + * + * GENERATE TVM STACK CODE + * + */ + +StackLayout Stack::vars() const { + StackLayout res; + res.reserve(s.size()); + for (auto x : s) { + res.push_back(x.first); + } + return res; +} + +int Stack::find(var_idx_t var, int from) const { + for (int i = from; i < depth(); i++) { + if (at(i).first == var) { + return i; + } + } + return -1; +} + +// finds var in [from .. to) +int Stack::find(var_idx_t var, int from, int to) const { + for (int i = from; i < depth() && i < to; i++) { + if (at(i).first == var) { + return i; + } + } + return -1; +} + +// finds var outside [from .. to) +int Stack::find_outside(var_idx_t var, int from, int to) const { + from = std::max(from, 0); + if (from >= to) { + return find(var); + } else { + int t = find(var, 0, from); + return t >= 0 ? t : find(var, to); + } +} + +int Stack::find_const(const_idx_t cst, int from) const { + for (int i = from; i < depth(); i++) { + if (at(i).second == cst) { + return i; + } + } + return -1; +} + +void Stack::forget_const() { + for (auto& vc : s) { + if (vc.second != not_const) { + vc.second = not_const; + } + } +} + +void Stack::issue_pop(int i) { + validate(i); + o << AsmOp::Pop(i); + at(i) = get(0); + s.pop_back(); + modified(); +} + +void Stack::issue_push(int i) { + validate(i); + o << AsmOp::Push(i); + s.push_back(get(i)); + modified(); +} + +void Stack::issue_xchg(int i, int j) { + validate(i); + validate(j); + if (i != j && get(i) != get(j)) { + o << AsmOp::Xchg(i, j); + std::swap(at(i), at(j)); + modified(); + } +} + +int Stack::drop_vars_except(const VarDescrList& var_info, int excl_var) { + int dropped = 0, changes; + do { + changes = 0; + int n = depth(); + for (int i = 0; i < n; i++) { + var_idx_t idx = at(i).first; + if (((!var_info[idx] || var_info[idx]->is_unused()) && idx != excl_var) || find(idx, 0, i - 1) >= 0) { + // unneeded + issue_pop(i); + changes = 1; + break; + } + } + dropped += changes; + } while (changes); + return dropped; +} + +void Stack::show(int flags) { + std::ostringstream os; + for (auto i : s) { + os << ' '; + o.show_var_ext(os, i); + } + o << AsmOp::Comment(os.str()); + mode |= _Shown; +} + +void Stack::forget_var(var_idx_t idx) { + for (auto& x : s) { + if (x.first == idx) { + x = std::make_pair(_Garbage, not_const); + modified(); + } + } +} + +void Stack::push_new_var(var_idx_t idx) { + forget_var(idx); + s.emplace_back(idx, not_const); + modified(); +} + +void Stack::push_new_const(var_idx_t idx, const_idx_t cidx) { + forget_var(idx); + s.emplace_back(idx, cidx); + modified(); +} + +void Stack::assign_var(var_idx_t new_idx, var_idx_t old_idx) { + int i = find(old_idx); + assert(i >= 0 && "variable not found in stack"); + if (new_idx != old_idx) { + at(i).first = new_idx; + modified(); + } +} + +void Stack::do_copy_var(var_idx_t new_idx, var_idx_t old_idx) { + int i = find(old_idx); + assert(i >= 0 && "variable not found in stack"); + if (find(old_idx, i + 1) < 0) { + issue_push(i); + assert(at(0).first == old_idx); + } + assign_var(new_idx, old_idx); +} + +void Stack::enforce_state(const StackLayout& req_stack) { + int k = (int)req_stack.size(); + for (int i = 0; i < k; i++) { + var_idx_t x = req_stack[i]; + if (i < depth() && s[i].first == x) { + continue; + } + int j = find(x); + if (j >= depth() - i) { + issue_push(j); + j = 0; + } + issue_xchg(j, depth() - i - 1); + assert(s[i].first == x); + } + while (depth() > k) { + issue_pop(0); + } + assert(depth() == k); + for (int i = 0; i < k; i++) { + assert(s[i].first == req_stack[i]); + } +} + +void Stack::merge_const(const Stack& req_stack) { + assert(s.size() == req_stack.s.size()); + for (std::size_t i = 0; i < s.size(); i++) { + assert(s[i].first == req_stack.s[i].first); + if (s[i].second != req_stack.s[i].second) { + s[i].second = not_const; + } + } +} + +void Stack::merge_state(const Stack& req_stack) { + enforce_state(req_stack.vars()); + merge_const(req_stack); +} + +void Stack::rearrange_top(const StackLayout& top, std::vector last) { + while (last.size() < top.size()) { + last.push_back(false); + } + int k = (int)top.size(); + for (int i = 0; i < k; i++) { + for (int j = i + 1; j < k; j++) { + if (top[i] == top[j]) { + last[i] = false; + break; + } + } + } + int ss = 0; + for (int i = 0; i < k; i++) { + if (last[i]) { + ++ss; + } + } + for (int i = 0; i < k; i++) { + var_idx_t x = top[i]; + // find s(j) containing x with j not in [ss, ss+i) + int j = find_outside(x, ss, ss + i); + if (last[i]) { + // rearrange x to be at s(ss-1) + issue_xchg(--ss, j); + assert(get(ss).first == x); + } else { + // create a new copy of x + issue_push(j); + issue_xchg(0, ss); + assert(get(ss).first == x); + } + } + assert(!ss); +} + +void Stack::rearrange_top(var_idx_t top, bool last) { + int i = find(top); + if (last) { + issue_xchg(0, i); + } else { + issue_push(i); + } + assert(get(0).first == top); +} + +bool Op::generate_code_step(Stack& stack) { + stack.opt_show(); + stack.drop_vars_except(var_info); + stack.opt_show(); + const auto& next_var_info = next->var_info; + switch (cl) { + case _Nop: + case _Import: + return true; + case _Return: { + stack.enforce_state(left); + stack.opt_show(); + return false; + } + case _IntConst: { + auto p = next_var_info[left[0]]; + if (!p || p->is_unused()) { + return true; + } + auto cidx = stack.o.register_const(int_const); + int i = stack.find_const(cidx); + if (i < 0) { + stack.o << push_const(int_const); + stack.push_new_const(left[0], cidx); + } else { + assert(stack.at(i).second == cidx); + stack.do_copy_var(left[0], stack[i]); + } + return true; + } + case _GlobVar: { + assert(left.size() == 1); + auto p = next_var_info[left[0]]; + if (!p || p->is_unused() || disabled()) { + return true; + } + auto func = dynamic_cast(fun_ref->value); + if (func) { + std::vector res; + res.reserve(left.size()); + for (var_idx_t i : left) { + res.emplace_back(i); + } + stack.o << func->compile(res, args); // compile res := f (args) + } else { + std::string name = sym::symbols.get_name(fun_ref->sym_idx); + stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size()); + } + stack.push_new_var(left[0]); + return true; + } + case _Let: { + assert(left.size() == right.size()); + int i = 0; + std::vector active; + active.reserve(left.size()); + for (std::size_t k = 0; k < left.size(); k++) { + var_idx_t y = left[k]; // "y" = "x" + auto p = next_var_info[y]; + active.push_back(p && !p->is_unused()); + } + for (std::size_t k = 0; k < left.size(); k++) { + if (!active[k]) { + continue; + } + var_idx_t x = right[k]; // "y" = "x" + bool is_last = true; + for (std::size_t l = k + 1; l < right.size(); l++) { + if (right[l] == x && active[l]) { + is_last = false; + } + } + if (is_last) { + auto info = var_info[x]; + is_last = (info && info->is_last()); + } + if (is_last) { + stack.assign_var(--i, x); + } else { + stack.do_copy_var(--i, x); + } + } + i = 0; + for (std::size_t k = 0; k < left.size(); k++) { + if (active[k]) { + stack.assign_var(left[k], --i); + } + } + return true; + } + case _Call: + case _CallInd: { + if (disabled()) { + return true; + } + SymValFunc* func = (fun_ref ? dynamic_cast(fun_ref->value) : nullptr); + auto arg_order = (func ? func->get_arg_order() : nullptr); + auto ret_order = (func ? func->get_ret_order() : nullptr); + assert(!arg_order || arg_order->size() == right.size()); + assert(!ret_order || ret_order->size() == left.size()); + std::vector right1; + if (args.size()) { + assert(args.size() == right.size()); + for (int i = 0; i < (int)right.size(); i++) { + int j = arg_order ? arg_order->at(i) : i; + const VarDescr& arg = args.at(j); + if (!arg.is_unused()) { + assert(var_info[arg.idx] && !var_info[arg.idx]->is_unused()); + right1.push_back(arg.idx); + } + } + } else if (arg_order) { + for (int i = 0; i < (int)right.size(); i++) { + right1.push_back(right.at(arg_order->at(i))); + } + } else { + right1 = right; + } + std::vector last; + for (var_idx_t x : right1) { + last.push_back(var_info[x] && var_info[x]->is_last()); + } + stack.rearrange_top(right1, std::move(last)); + stack.opt_show(); + int k = (int)stack.depth() - (int)right1.size(); + assert(k >= 0); + for (int i = 0; i < (int)right1.size(); i++) { + if (stack.s[k + i].first != right1[i]) { + std::cerr << stack.o; + } + assert(stack.s[k + i].first == right1[i]); + } + if (cl == _CallInd) { + stack.o << exec_arg_op("CALLARGS", (int)right.size() - 1, -1, (int)right.size() - 1); + } else { + auto func = dynamic_cast(fun_ref->value); + if (func) { + std::vector res; + res.reserve(left.size()); + for (var_idx_t i : left) { + res.emplace_back(i); + } + stack.o << func->compile(res, args); // compile res := f (args) + } else { + std::string name = sym::symbols.get_name(fun_ref->sym_idx); + stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size()); + } + } + stack.s.resize(k); + for (int i = 0; i < (int)left.size(); i++) { + int j = ret_order ? ret_order->at(i) : i; + stack.push_new_var(left.at(j)); + } + return true; + } + case _If: { + if (block0->is_empty() && block1->is_empty()) { + return true; + } + var_idx_t x = left[0]; + stack.rearrange_top(x, var_info[x] && var_info[x]->is_last()); + assert(stack[0] == x); + stack.opt_show(); + stack.s.pop_back(); + stack.modified(); + if (block1->is_empty()) { + // if (left) block0; ... + if (block0->noreturn()) { + stack.o << "IFJMP:<{"; + stack.o.indent(); + Stack stack_copy{stack}; + block0->generate_code_all(stack_copy); + stack.o.undent(); + stack.o << "}>"; + return true; + } + stack.o << "IF:<{"; + stack.o.indent(); + Stack stack_copy{stack}; + block0->generate_code_all(stack_copy); + stack_copy.drop_vars_except(var_info); + stack_copy.opt_show(); + if (stack_copy == stack) { + stack.o.undent(); + stack.o << "}>"; + return true; + } + stack_copy.drop_vars_except(next->var_info); + stack_copy.opt_show(); + if (stack_copy.vars() == stack.vars()) { + stack.o.undent(); + stack.o << "}>"; + stack.merge_const(stack_copy); + return true; + } + stack.o.undent(); + stack.o << "}>ELSE<{"; + stack.o.indent(); + stack.merge_state(stack_copy); + stack.opt_show(); + stack.o.undent(); + stack.o << "}>"; + return true; + } + if (block0->is_empty()) { + // if (!left) block1; ... + if (block1->noreturn()) { + stack.o << "IFNOTJMP:<{"; + stack.o.indent(); + Stack stack_copy{stack}; + block1->generate_code_all(stack_copy); + stack.o.undent(); + stack.o << "}>"; + return true; + } + stack.o << "IFNOT:<{"; + stack.o.indent(); + Stack stack_copy{stack}; + block1->generate_code_all(stack_copy); + stack_copy.drop_vars_except(var_info); + stack_copy.opt_show(); + if (stack_copy.vars() == stack.vars()) { + stack.o.undent(); + stack.o << "}>"; + stack.merge_const(stack_copy); + return true; + } + stack_copy.drop_vars_except(next->var_info); + stack_copy.opt_show(); + if (stack_copy.vars() == stack.vars()) { + stack.o.undent(); + stack.o << "}>"; + stack.merge_const(stack_copy); + return true; + } + stack.o.undent(); + stack.o << "}>ELSE<{"; + stack.o.indent(); + stack.merge_state(stack_copy); + stack.opt_show(); + stack.o.undent(); + stack.o << "}>"; + return true; + } + if (block0->noreturn()) { + stack.o << "IFJMP:<{"; + stack.o.indent(); + Stack stack_copy{stack}; + block0->generate_code_all(stack_copy); + stack.o.undent(); + stack.o << "}>"; + return block1->generate_code_all(stack); + } + if (block1->noreturn()) { + stack.o << "IFNOTJMP:<{"; + stack.o.indent(); + Stack stack_copy{stack}; + block1->generate_code_all(stack_copy); + stack.o.undent(); + stack.o << "}>"; + return block0->generate_code_all(stack); + } + stack.o << "IF:<{"; + stack.o.indent(); + Stack stack_copy{stack}; + block0->generate_code_all(stack_copy); + stack_copy.drop_vars_except(next->var_info); + stack_copy.opt_show(); + stack.o.undent(); + stack.o << "}>ELSE<{"; + stack.o.indent(); + block1->generate_code_all(stack); + stack.merge_state(stack_copy); + stack.opt_show(); + stack.o.undent(); + stack.o << "}>"; + return true; + } + case _Repeat: { + var_idx_t x = left[0]; + //stack.drop_vars_except(block0->var_info, x); + stack.rearrange_top(x, var_info[x] && var_info[x]->is_last()); + assert(stack[0] == x); + stack.opt_show(); + stack.s.pop_back(); + stack.modified(); + if (!next->is_empty()) { + stack.o << "REPEAT:<{"; + stack.o.indent(); + stack.forget_const(); + StackLayout layout1 = stack.vars(); + block0->generate_code_all(stack); + stack.enforce_state(std::move(layout1)); + stack.opt_show(); + stack.o.undent(); + stack.o << "}>"; + return true; + } else { + stack.o << "REPEATEND"; + stack.forget_const(); + StackLayout layout1 = stack.vars(); + block0->generate_code_all(stack); + stack.enforce_state(std::move(layout1)); + stack.opt_show(); + return false; + } + } + case _Again: { + stack.drop_vars_except(block0->var_info); + stack.opt_show(); + if (!next->is_empty()) { + stack.o << "AGAIN:<{"; + stack.o.indent(); + stack.forget_const(); + StackLayout layout1 = stack.vars(); + block0->generate_code_all(stack); + stack.enforce_state(std::move(layout1)); + stack.opt_show(); + stack.o.undent(); + stack.o << "}>"; + return true; + } else { + stack.o << "AGAINEND"; + stack.forget_const(); + StackLayout layout1 = stack.vars(); + block0->generate_code_all(stack); + stack.enforce_state(std::move(layout1)); + stack.opt_show(); + return false; + } + } + case _Until: { + // stack.drop_vars_except(block0->var_info); + // stack.opt_show(); + if (!next->is_empty()) { + stack.o << "UNTIL:<{"; + stack.o.indent(); + stack.forget_const(); + auto layout1 = stack.vars(); + block0->generate_code_all(stack); + layout1.push_back(left[0]); + stack.enforce_state(std::move(layout1)); + stack.opt_show(); + stack.o.undent(); + stack.o << "}>"; + stack.s.pop_back(); + stack.modified(); + return true; + } else { + stack.o << "UNTILEND"; + stack.forget_const(); + StackLayout layout1 = stack.vars(); + block0->generate_code_all(stack); + layout1.push_back(left[0]); + stack.enforce_state(std::move(layout1)); + stack.opt_show(); + return false; + } + } + case _While: { + // while (block0 | left) block1; ...next + var_idx_t x = left[0]; + stack.drop_vars_except(block0->var_info); + stack.opt_show(); + StackLayout layout1 = stack.vars(); + bool next_empty = next->is_empty(); + stack.o << (next_empty ? "WHILEEND:<{" : "WHILE:<{"); + stack.o.indent(); + stack.forget_const(); + block0->generate_code_all(stack); + stack.rearrange_top(x, !next->var_info[x] && !block1->var_info[x]); + stack.opt_show(); + stack.s.pop_back(); + stack.modified(); + stack.o.undent(); + Stack stack_copy{stack}; + stack.o << (next_empty ? "}>" : "}>DO<{"); + if (!next_empty) { + stack.o.indent(); + } + stack_copy.opt_show(); + block1->generate_code_all(stack_copy); + stack_copy.enforce_state(std::move(layout1)); + stack_copy.opt_show(); + if (!next_empty) { + stack.o.undent(); + stack.o << "}>"; + return true; + } else { + return false; + } + } + default: + std::cerr << "fatal: unknown operation \n"; + throw src::ParseError{where, "unknown operation in generate_code()"}; + } +} + +bool Op::generate_code_all(Stack& stack) { + if (generate_code_step(stack) && next) { + return next->generate_code_all(stack); + } else { + return false; + } +} + +void CodeBlob::generate_code(AsmOpList& out, int mode) { + Stack stack{out, mode}; + assert(ops && ops->cl == Op::_Import); + for (var_idx_t x : ops->left) { + stack.push_new_var(x); + } + ops->generate_code_all(stack); + optimize_code(out); +} + +void CodeBlob::generate_code(std::ostream& os, int mode, int indent) { + AsmOpList out_list(indent, &vars); + generate_code(out_list, mode); + out_list.out(os, mode); +} + +} // namespace funC diff --git a/ton-test-liteclient-full/lite-client/crypto/func/func.cpp b/ton-test-liteclient-full/lite-client/crypto/func/func.cpp index 582c2ad..2eb9a09 100644 --- a/ton-test-liteclient-full/lite-client/crypto/func/func.cpp +++ b/ton-test-liteclient-full/lite-client/crypto/func/func.cpp @@ -1,3930 +1,43 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "common/refcnt.hpp" -#include "common/bigint.hpp" -#include "common/refint.h" -#include "srcread.hpp" -#include "lexer.hpp" -#include "symtable.hpp" #include "func.h" - -namespace src { - -int lexem_is_special(std::string str) { - return 0; // no special lexems -} - -int compute_symbol_subclass(std::string str) { - return 0; // no symbol/identifier subclasses -} - -/* - * - * KEYWORD DEFINITION - * - */ - -void define_keywords() { - symbols.add_kw_char('+'); - symbols.add_kw_char('-'); - symbols.add_kw_char('*'); - symbols.add_kw_char('/'); - symbols.add_kw_char('%'); - symbols.add_kw_char(':'); - symbols.add_kw_char(','); - symbols.add_kw_char(';'); - symbols.add_kw_char('('); - symbols.add_kw_char(')'); - symbols.add_kw_char('{'); - symbols.add_kw_char('}'); - symbols.add_kw_char('='); - symbols.add_kw_char('_'); - symbols.add_kw_char('<'); - symbols.add_kw_char('>'); - symbols.add_kw_char('&'); - symbols.add_kw_char('|'); - - using Kw = funC::Keyword; - symbols.add_keyword("==", Kw::_Eq); - symbols.add_keyword("!=", Kw::_Neq); - symbols.add_keyword("<=", Kw::_Leq); - symbols.add_keyword(">=", Kw::_Geq); - symbols.add_keyword("<<", Kw::_Lshift); - symbols.add_keyword(">>", Kw::_Rshift); - symbols.add_keyword(">>~", Kw::_RshiftR); - symbols.add_keyword(">>^", Kw::_RshiftC); - symbols.add_keyword("/~", Kw::_DivR); - symbols.add_keyword("/^", Kw::_DivC); - symbols.add_keyword("/%", Kw::_DivMod); - symbols.add_keyword("+=", Kw::_PlusLet); - symbols.add_keyword("-=", Kw::_MinusLet); - symbols.add_keyword("*=", Kw::_TimesLet); - symbols.add_keyword("/=", Kw::_DivLet); - symbols.add_keyword("%=", Kw::_ModLet); - symbols.add_keyword("/~=", Kw::_DivRLet); - symbols.add_keyword("/^=", Kw::_DivCLet); - symbols.add_keyword("<<=", Kw::_LshiftLet); - symbols.add_keyword(">>=", Kw::_RshiftLet); - symbols.add_keyword(">>~=", Kw::_RshiftRLet); - symbols.add_keyword(">>^=", Kw::_RshiftCLet); - - symbols.add_keyword("return", Kw::_Return); - symbols.add_keyword("var", Kw::_Var); - symbols.add_keyword("repeat", Kw::_Repeat); - symbols.add_keyword("do", Kw::_Do); - symbols.add_keyword("while", Kw::_While); - symbols.add_keyword("until", Kw::_Until); - symbols.add_keyword("if", Kw::_If); - symbols.add_keyword("then", Kw::_Then); - symbols.add_keyword("else", Kw::_Else); - symbols.add_keyword("elseif", Kw::_Elseif); - - symbols.add_keyword("int", Kw::_Int); - symbols.add_keyword("cell", Kw::_Cell); - symbols.add_keyword("slice", Kw::_Slice); - symbols.add_keyword("builder", Kw::_Builder); - symbols.add_keyword("cont", Kw::_Cont); - symbols.add_keyword("->", Kw::_Mapsto); - - symbols.add_keyword("extern", Kw::_Extern); - symbols.add_keyword("operator", Kw::_Operator); - symbols.add_keyword("infix", Kw::_Infix); - symbols.add_keyword("infixl", Kw::_Infixl); - symbols.add_keyword("infixr", Kw::_Infixr); -} - -} // namespace src +#include "parser/srcread.h" +#include "parser/lexer.h" +#include "parser/symtable.h" +#include namespace funC { -using src::Lexer; -using src::symbols; -using td::Ref; - -/* - * - * TYPE EXPRESSIONS - * - */ - -int TypeExpr::holes = 0; // not thread safe, but it is ok for now - -void TypeExpr::compute_width() { - switch (constr) { - case te_Atomic: - case te_Map: - minw = maxw = 1; - break; - case te_Tensor: - minw = maxw = 0; - for (TypeExpr* arg : args) { - minw += arg->minw; - maxw += arg->maxw; - } - if (minw > w_inf) { - minw = w_inf; - } - if (maxw > w_inf) { - maxw = w_inf; - } - break; - case te_Indirect: - minw = args[0]->minw; - maxw = args[0]->maxw; - break; - default: - minw = 0; - maxw = w_inf; - break; - } -} - -bool TypeExpr::recompute_width() { - switch (constr) { - case te_Tensor: - case te_Indirect: { - int min = 0, max = 0; - for (TypeExpr* arg : args) { - min += arg->minw; - max += arg->maxw; - } - if (min > maxw || max < minw) { - return false; - } - if (min > w_inf) { - min = w_inf; - } - if (max > w_inf) { - max = w_inf; - } - if (minw < min) { - minw = min; - } - if (maxw > max) { - maxw = max; - } - return true; - } - default: - return false; - } -} - -int TypeExpr::extract_components(std::vector& comp_list) { - if (constr != te_Indirect && constr != te_Tensor) { - comp_list.push_back(this); - return 1; - } - int res = 0; - for (TypeExpr* arg : args) { - res += arg->extract_components(comp_list); - } - return res; -} - -TypeExpr* TypeExpr::new_map(TypeExpr* from, TypeExpr* to) { - return new TypeExpr{te_Map, std::vector{from, to}}; -} - -void TypeExpr::replace_with(TypeExpr* te2) { - if (te2 == this) { - return; - } - constr = te_Indirect; - value = 0; - minw = te2->minw; - maxw = te2->maxw; - args.clear(); - args.push_back(te2); -} - -bool TypeExpr::remove_indirect(TypeExpr*& te, TypeExpr* forbidden) { - assert(te); - while (te->constr == te_Indirect) { - te = te->args[0]; - } - if (te->constr == te_Unknown) { - return te == forbidden; - } - bool res = false; - for (auto& x : te->args) { - res |= remove_indirect(x, forbidden); - } - return res; -} - -void TypeExpr::show_width(std::ostream& os) { - os << minw; - if (maxw != minw) { - os << ".."; - if (maxw < w_inf) { - os << maxw; - } - } -} - -std::ostream& operator<<(std::ostream& os, TypeExpr* type_expr) { - if (!type_expr) { - return os << "(null-type-ptr)"; - } - return type_expr->print(os); -} - -std::ostream& TypeExpr::print(std::ostream& os, int lex_level) { - switch (constr) { - case te_Unknown: - return os << "??" << value; - case te_Indirect: - return os << args[0]; - case te_Atomic: { - switch (value) { - case _Int: - return os << "int"; - case _Cell: - return os << "cell"; - case _Slice: - return os << "slice"; - case _Builder: - return os << "builder"; - case _Cont: - return os << "cont"; - case _Type: - return os << "type"; - default: - return os << "atomic-type-" << value; - } - } - case te_Tensor: { - os << "("; - auto c = args.size(); - if (c) { - for (const auto& x : args) { - x->print(os); - if (--c) { - os << ", "; - } - } - } - return os << ")"; - } - case te_Map: { - assert(args.size() == 2); - if (lex_level > 0) { - os << "("; - } - args[0]->print(os, 1); - os << " -> "; - args[1]->print(os); - if (lex_level > 0) { - os << ")"; - } - return os; - } - default: - return os << "unknown-type-expr-" << constr; - } -} - -struct UniformizeError { - TypeExpr* te1; - TypeExpr* te2; - std::string msg; - UniformizeError(TypeExpr* _te1, TypeExpr* _te2, std::string _msg = "") : te1(_te1), te2(_te2), msg(_msg) { - } - void print_message(std::ostream& os) const; - std::string message() const; -}; - -void UniformizeError::print_message(std::ostream& os) const { - os << "cannot unify type " << te1 << " with " << te2; - if (!msg.empty()) { - os << ": " << msg; - } -} - -std::ostream& operator<<(std::ostream& os, const UniformizeError& ue) { - ue.print_message(os); - return os; -} - -std::string UniformizeError::message() const { - std::ostringstream os; - UniformizeError::print_message(os); - return os.str(); -} - -void check_width_compat(TypeExpr* te1, TypeExpr* te2) { - if (te1->minw > te2->maxw || te2->minw > te1->maxw) { - std::ostringstream os{"cannot uniformize types of widths "}; - te1->show_width(os); - os << " and "; - te2->show_width(os); - throw UniformizeError{te1, te2, os.str()}; - } -} - -void check_update_widths(TypeExpr* te1, TypeExpr* te2) { - check_width_compat(te1, te2); - te1->minw = te2->minw = std::max(te1->minw, te2->minw); - te1->maxw = te2->maxw = std::min(te1->maxw, te2->maxw); - assert(te1->minw <= te2->minw); -} - -void uniformize(TypeExpr*& te1, TypeExpr*& te2) { - assert(te1 && te2); - // std::cerr << "uniformize( " << te1 << " , " << te2 << " )\n"; - while (te1->constr == TypeExpr::te_Indirect) { - te1 = te1->args[0]; - } - while (te2->constr == TypeExpr::te_Indirect) { - te2 = te2->args[0]; - } - if (te1 == te2) { - return; - } - if (te1->constr == TypeExpr::te_Unknown) { - if (te2->constr == TypeExpr::te_Unknown) { - assert(te1->value != te2->value); - } - if (TypeExpr::remove_indirect(te2, te1)) { - throw UniformizeError{te1, te2, "uniformization results in an infinite cyclic type"}; - } - check_update_widths(te1, te2); - te1->replace_with(te2); - te1 = te2; - return; - } - if (te2->constr == TypeExpr::te_Unknown) { - if (TypeExpr::remove_indirect(te1, te2)) { - throw UniformizeError{te2, te1, "uniformization results in an infinite cyclic type"}; - } - check_update_widths(te2, te1); - te2->replace_with(te1); - te2 = te1; - return; - } - if (te1->constr != te2->constr || te1->value != te2->value || te1->args.size() != te2->args.size()) { - throw UniformizeError{te1, te2}; - } - for (std::size_t i = 0; i < te1->args.size(); i++) { - uniformize(te1->args[i], te2->args[i]); - } - if (te1->constr == TypeExpr::te_Tensor) { - if (!te1->recompute_width()) { - throw UniformizeError{te1, te2, "uniformization incompatible with known width of first type"}; - } - if (!te2->recompute_width()) { - throw UniformizeError{te2, te1, "uniformization incompatible with known width of first type"}; - } - check_update_widths(te1, te2); - } - te1->replace_with(te2); - te1 = te2; -} - -/* - * - * ABSTRACT CODE - * - */ - -TmpVar::TmpVar(var_idx_t _idx, int _cls, TypeExpr* _type, SymDef* sym, const SrcLocation* loc) - : v_type(_type), idx(_idx), cls(_cls), coord(0) { - if (sym) { - name = sym->sym_idx; - sym->value->idx = _idx; - } - if (loc) { - where = std::make_unique(*loc); - } - if (!_type) { - v_type = TypeExpr::new_hole(); - } -} - -void TmpVar::set_location(const SrcLocation& loc) { - if (where) { - *where = loc; - } else { - where = std::make_unique(loc); - } -} - -void TmpVar::dump(std::ostream& os) const { - show(os); - os << " : " << v_type << " (width "; - v_type->show_width(os); - os << ")"; - if (coord > 0) { - os << " = _" << (coord >> 8) << '.' << (coord & 255); - } else if (coord < 0) { - int n = (~coord >> 8), k = (~coord & 0xff); - if (k) { - os << " = (_" << n << ".._" << (n + k - 1) << ")"; - } else { - os << " = ()"; - } - } - os << std::endl; -} - -void TmpVar::show(std::ostream& os, int omit_idx) const { - if (cls & _Named) { - os << symbols.get_name(name); - if (omit_idx && (omit_idx >= 2 || (cls & _UniqueName))) { - return; - } - } - os << '_' << idx; -} - -std::ostream& operator<<(std::ostream& os, const TmpVar& var) { - var.show(os); - return os; -} - -void VarDescr::show_value(std::ostream& os) const { - if (val & _Int) { - os << 'i'; - } - if (val & _Const) { - os << 'c'; - } - if (val & _Zero) { - os << '0'; - } - if (val & _NonZero) { - os << '!'; - } - if (val & _Pos) { - os << '>'; - } - if (val & _Neg) { - os << '<'; - } - if (val & _Bool) { - os << 'B'; - } - if (val & _Bit) { - os << 'b'; - } - if (val & _Even) { - os << 'E'; - } - if (val & _Odd) { - os << 'O'; - } - if (val & _Finite) { - os << 'f'; - } - if (val & _Nan) { - os << 'N'; - } - if (int_const.not_null()) { - os << '=' << int_const; - } -} - -void VarDescr::set_const(td::RefInt256 value) { - int_const = std::move(value); - if (!int_const->signed_fits_bits(257)) { - int_const.write().invalidate(); - } - val = _Const | _Int; - int s = sgn(int_const); - if (s < -1) { - val |= _Nan | _NonZero; - } else if (s < 0) { - val |= _NonZero | _Neg | _Finite; - if (*int_const == -1) { - val |= _Bool; - } - } else if (s > 0) { - val |= _NonZero | _Pos | _Finite; - } else if (!s) { - if (*int_const == 1) { - val |= _Bit; - } - val |= _Zero | _Neg | _Pos | _Finite | _Bool | _Bit; - } - if (val & _Finite) { - val |= int_const->get_bit(0) ? _Odd : _Even; - } -} - -void VarDescr::set_const_nan() { - set_const(td::RefInt256{true}); -} - -void VarDescr::operator|=(const VarDescr& y) { - val &= y.val; - if (is_int_const() && cmp(int_const, y.int_const) != 0) { - val &= ~_Const; - } - if (!(val & _Const)) { - int_const.clear(); - } -} - -void VarDescr::operator&=(const VarDescr& y) { - val |= y.val; - if (y.int_const.not_null() && int_const.is_null()) { - int_const = y.int_const; - } -} - -void VarDescr::set_value(const VarDescr& y) { - val = y.val; - int_const = y.int_const; -} - -void VarDescr::set_value(VarDescr&& y) { - val = y.val; - int_const = std::move(y.int_const); -} - -void VarDescr::clear_value() { - val = 0; - int_const.clear(); -} - -ListIterator begin(const std::unique_ptr& op_list) { - return ListIterator{op_list.get()}; -} - -ListIterator end(const std::unique_ptr& op_list) { - return ListIterator{}; -} - -ListIterator cbegin(const Op* op_list) { - return ListIterator{op_list}; -} - -ListIterator cend(const Op* op_list) { - return ListIterator{}; -} - -ListIterator begin(const Op* op_list) { - return ListIterator{op_list}; -} - -ListIterator end(const Op* op_list) { - return ListIterator{}; -} - -ListIterator begin(Op* op_list) { - return ListIterator{op_list}; -} - -ListIterator end(Op* op_list) { - return ListIterator{}; -} - -void Op::flags_set_clear(int set, int clear) { - flags = (flags | set) & ~clear; - for (auto& op : block0) { - op.flags_set_clear(set, clear); - } - for (auto& op : block1) { - op.flags_set_clear(set, clear); - } -} -void Op::split_vars(const std::vector& vars) { - split_var_list(left, vars); - split_var_list(right, vars); - for (auto& op : block0) { - op.split_vars(vars); - } - for (auto& op : block1) { - op.split_vars(vars); - } -} - -void Op::split_var_list(std::vector& var_list, const std::vector& vars) { - int new_size = 0, changes = 0; - for (var_idx_t v : var_list) { - int c = vars.at(v).coord; - if (c < 0) { - ++changes; - new_size += (~c & 0xff); - } else { - ++new_size; - } - } - if (!changes) { - return; - } - std::vector new_var_list; - new_var_list.reserve(new_size); - for (var_idx_t v : var_list) { - int c = vars.at(v).coord; - if (c < 0) { - int n = (~c >> 8), k = (~c & 0xff); - while (k-- > 0) { - new_var_list.push_back(n++); - } - } else { - new_var_list.push_back(v); - } - } - var_list = std::move(new_var_list); -} - -void Op::show(std::ostream& os, const std::vector& vars, std::string pfx, int mode) const { - if (mode & 2) { - os << pfx << " ["; - for (const auto& v : var_info.list) { - os << ' '; - if (v.flags & VarDescr::_Last) { - os << '*'; - } - os << vars[v.idx]; - if (mode & 4) { - os << ':'; - v.show_value(os); - } - } - os << " ]\n"; - } - std::string dis = disabled() ? " " : ""; - if (noreturn()) { - dis += " "; - } - switch (cl) { - case _Undef: - os << pfx << dis << "???\n"; - break; - case _Nop: - os << pfx << dis << "NOP\n"; - break; - case _Call: - os << pfx << dis << "CALL: "; - show_var_list(os, left, vars); - os << " := " << (fun_ref ? fun_ref->name() : "(null)") << " "; - if ((mode & 4) && args.size() == right.size()) { - show_var_list(os, args, vars); - } else { - show_var_list(os, right, vars); - } - os << std::endl; - break; - case _CallInd: - os << pfx << dis << "CALLIND: "; - show_var_list(os, left, vars); - os << " := EXEC "; - show_var_list(os, right, vars); - os << std::endl; - break; - case _Let: - os << pfx << dis << "LET "; - show_var_list(os, left, vars); - os << " := "; - show_var_list(os, right, vars); - os << std::endl; - break; - case _IntConst: - os << pfx << dis << "CONST "; - show_var_list(os, left, vars); - os << " := " << int_const << std::endl; - break; - case _Import: - os << pfx << dis << "IMPORT "; - show_var_list(os, left, vars); - os << std::endl; - break; - case _Return: - os << pfx << dis << "RETURN "; - show_var_list(os, left, vars); - os << std::endl; - break; - case _GlobVar: - os << pfx << dis << "GLOBVAR "; - show_var_list(os, left, vars); - os << " := " << (fun_ref ? fun_ref->name() : "(null)") << std::endl; - break; - case _Repeat: - os << pfx << dis << "REPEAT "; - show_var_list(os, left, vars); - os << ' '; - show_block(os, block0.get(), vars, pfx); - os << std::endl; - break; - case _If: - os << pfx << dis << "IF "; - show_var_list(os, left, vars); - os << ' '; - show_block(os, block0.get(), vars, pfx); - os << " ELSE "; - show_block(os, block1.get(), vars, pfx); - os << std::endl; - break; - case _While: - os << pfx << dis << "WHILE "; - show_var_list(os, left, vars); - os << ' '; - show_block(os, block0.get(), vars, pfx); - os << " DO "; - show_block(os, block1.get(), vars, pfx); - os << std::endl; - break; - case _Until: - os << pfx << dis << "UNTIL "; - show_var_list(os, left, vars); - os << ' '; - show_block(os, block0.get(), vars, pfx); - os << std::endl; - break; - case _Again: - os << pfx << dis << "AGAIN "; - show_var_list(os, left, vars); - os << ' '; - show_block(os, block0.get(), vars, pfx); - os << std::endl; - break; - default: - os << pfx << dis << " "; - show_var_list(os, left, vars); - os << " -- "; - show_var_list(os, right, vars); - os << std::endl; - break; - } -} - -void Op::show_var_list(std::ostream& os, const std::vector& idx_list, - const std::vector& vars) const { - if (!idx_list.size()) { - os << "()"; - } else if (idx_list.size() == 1) { - os << vars.at(idx_list[0]); - } else { - os << "(" << vars.at(idx_list[0]); - for (std::size_t i = 1; i < idx_list.size(); i++) { - os << "," << vars.at(idx_list[i]); - } - os << ")"; - } -} - -void Op::show_var_list(std::ostream& os, const std::vector& list, const std::vector& vars) const { - auto n = list.size(); - if (!n) { - os << "()"; - } else { - os << "( "; - for (std::size_t i = 0; i < list.size(); i++) { - if (i) { - os << ", "; - } - if (list[i].is_unused()) { - os << '?'; - } - os << vars.at(list[i].idx) << ':'; - list[i].show_value(os); - } - os << " )"; - } -} - -void Op::show_block(std::ostream& os, const Op* block, const std::vector& vars, std::string pfx, int mode) { - os << "{" << std::endl; - std::string pfx2 = pfx + " "; - for (const Op& op : block) { - op.show(os, vars, pfx2, mode); - } - os << pfx << "}"; -} - -void CodeBlob::flags_set_clear(int set, int clear) { - for (auto& op : ops) { - op.flags_set_clear(set, clear); - } -} - -std::ostream& operator<<(std::ostream& os, const CodeBlob& code) { - code.print(os); - return os; -} - -void CodeBlob::print(std::ostream& os, int flags) const { - os << "CODE BLOB: " << var_cnt << " variables, " << in_var_cnt << " input\n"; - for (const auto& var : vars) { - var.dump(os); - if (var.where && (flags & 1) != 0) { - var.where->show(os); - os << " defined here:\n"; - var.where->show_context(os); - } - } - os << "------- BEGIN --------\n"; - for (const auto& op : ops) { - op.show(os, vars, "", flags); - } - os << "-------- END ---------\n\n"; -} - -var_idx_t CodeBlob::create_var(int cls, TypeExpr* var_type, SymDef* sym, const SrcLocation* location) { - vars.emplace_back(var_cnt, cls, var_type, sym, location); - if (sym) { - sym->value->idx = var_cnt; - } - return var_cnt++; -} - -bool CodeBlob::import_params(FormalArgList arg_list) { - if (var_cnt || in_var_cnt || op_cnt) { - return false; - } - std::vector list; - for (const auto& par : arg_list) { - TypeExpr* arg_type; - SymDef* arg_sym; - SrcLocation arg_loc; - std::tie(arg_type, arg_sym, arg_loc) = par; - list.push_back(create_var(arg_sym ? (TmpVar::_In | TmpVar::_Named) : TmpVar::_In, arg_type, arg_sym, &arg_loc)); - } - emplace_back(loc, Op::_Import, list); - in_var_cnt = var_cnt; - return true; -} - -/* - * - * SYMBOL VALUES - * - */ - -int glob_func_cnt, undef_func_cnt; -std::vector glob_func; - -SymDef* predefine_builtin_func(std::string name, TypeExpr* func_type) { - sym_idx_t name_idx = symbols.lookup(name, 1); - if (symbols.is_keyword(name_idx)) { - std::cerr << "fatal: global function `" << name << "` already defined as a keyword" << std::endl; - } - SymDef* def = sym::define_global_symbol(name_idx, true); - if (!def) { - std::cerr << "fatal: global function `" << name << "` already defined" << std::endl; - std::exit(1); - } - return def; -} - -void define_builtin_func(std::string name, TypeExpr* func_type, const AsmOp& macro) { - SymDef* def = predefine_builtin_func(name, func_type); - def->value = new SymValBuiltinFunc{func_type, macro}; -} - -void define_builtin_func(std::string name, TypeExpr* func_type, const simple_compile_func_t func) { - SymDef* def = predefine_builtin_func(name, func_type); - def->value = new SymValBuiltinFunc{func_type, func}; -} /* * - * EXPRESSIONS + * OUTPUT CODE GENERATOR * */ -Expr* Expr::copy() const { - auto res = new Expr{*this}; - for (auto& arg : res->args) { - arg = arg->copy(); - } - return res; -} - -Expr::Expr(int c, sym_idx_t name_idx, std::initializer_list _arglist) - : cls(c), val(0), sym(nullptr), e_type(nullptr), args(std::move(_arglist)) { - sym = sym::lookup_symbol(name_idx); - if (!sym) { - } -} - -void Expr::chk_rvalue(const Lexem& lem) const { - if (!is_rvalue()) { - lem.error_at("rvalue expected before `", "`"); - } -} - -void Expr::chk_lvalue(const Lexem& lem) const { - if (!is_lvalue()) { - lem.error_at("lvalue expected before `", "`"); - } -} - -void Expr::chk_type(const Lexem& lem) const { - if (!is_type()) { - lem.error_at("type expression expected before `", "`"); - } -} - -bool Expr::deduce_type(const Lexem& lem) { - if (e_type) { - return true; - } - switch (cls) { - case _Apply: { - if (!sym) { - return false; - } - SymVal* sym_val = dynamic_cast(sym->value); - if (!sym_val || !sym_val->get_type()) { - return false; - } - std::vector arg_types; - for (const auto& arg : args) { - arg_types.push_back(arg->e_type); - } - TypeExpr* fun_type = TypeExpr::new_map(TypeExpr::new_tensor(arg_types), TypeExpr::new_hole()); - try { - uniformize(fun_type, sym_val->sym_type); - } catch (UniformizeError& ue) { - std::ostringstream os; - os << "cannot apply function " << sym->name() << " : " << sym_val->get_type() << " to arguments of type " - << fun_type->args[0] << ": " << ue; - lem.error(os.str()); - } - e_type = fun_type->args[1]; - TypeExpr::remove_indirect(e_type); - return true; - } - case _VarApply: { - assert(args.size() == 2); - TypeExpr* fun_type = TypeExpr::new_map(args[1]->e_type, TypeExpr::new_hole()); - try { - uniformize(fun_type, args[0]->e_type); - } catch (UniformizeError& ue) { - std::ostringstream os; - os << "cannot apply expression of type " << args[0]->e_type << " to an expression of type " << args[1]->e_type - << ": " << ue; - lem.error(os.str()); - } - e_type = fun_type->args[1]; - TypeExpr::remove_indirect(e_type); - return true; - } - case _Letop: { - assert(args.size() == 2); - try { - // std::cerr << "in assignment: "; - uniformize(args[0]->e_type, args[1]->e_type); - } catch (UniformizeError& ue) { - std::ostringstream os; - os << "cannot assign an expression of type " << args[1]->e_type << " to a variable or pattern of type " - << args[0]->e_type << ": " << ue; - lem.error(os.str()); - } - e_type = args[0]->e_type; - TypeExpr::remove_indirect(e_type); - return true; - } - } - return false; -} - -int Expr::define_new_vars(CodeBlob& code) { - switch (cls) { - case _Tuple: - case _TypeApply: { - int res = 0; - for (const auto& x : args) { - res += x->define_new_vars(code); - } - return res; - } - case _Var: - if (val < 0) { - val = code.create_var(TmpVar::_Named, e_type, sym, &here); - return 1; - } - break; - case _Hole: - if (val < 0) { - val = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); - } - break; - } - return 0; -} - -int Expr::predefine_vars() { - switch (cls) { - case _Tuple: - case _TypeApply: { - int res = 0; - for (const auto& x : args) { - res += x->predefine_vars(); - } - return res; - } - case _Var: - if (!sym) { - assert(val < 0 && here.defined()); - sym = sym::define_symbol(~val, false, here); - if (!sym) { - throw src::ParseError{here, std::string{"redefined variable `"} + symbols.get_name(~val) + "`"}; - } - sym->value = new SymVal{SymVal::_Var, -1, e_type}; - return 1; - } - break; - } - return 0; -} - -std::vector Expr::pre_compile(CodeBlob& code) const { - switch (cls) { - case _Apply: - case _Tuple: { - std::vector res; - for (const auto& x : args) { - auto add = x->pre_compile(code); - res.insert(res.end(), add.cbegin(), add.cend()); - } - if (cls == _Tuple) { - return res; - } - var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); - std::vector rvect{rv}; - code.emplace_back(here, Op::_Call, rvect, std::move(res), sym); - return rvect; - } - case _TypeApply: - return args[0]->pre_compile(code); - case _Var: - case _Hole: - return {val}; - case _VarApply: - if (args[0]->cls == _Glob) { - std::vector res = args[1]->pre_compile(code); - var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); - std::vector rvect{rv}; - code.emplace_back(here, Op::_Call, rvect, std::move(res), args[0]->sym); - return rvect; - } else { - std::vector res = args[1]->pre_compile(code); - std::vector tfunc = args[0]->pre_compile(code); - if (tfunc.size() != 1) { - throw src::Fatal{"stack tuple used as a function"}; - } - res.push_back(tfunc[0]); - var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); - std::vector rvect{rv}; - code.emplace_back(here, Op::_CallInd, rvect, std::move(res)); - return rvect; - } - case _Const: { - var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); - std::vector rvect{rv}; - code.emplace_back(here, Op::_IntConst, rvect, intval); - return rvect; - } - case _Glob: { - var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); - std::vector rvect{rv}; - code.emplace_back(here, Op::_GlobVar, rvect, std::vector{}, sym); - return rvect; - } - case _Letop: { - std::vector right = args[1]->pre_compile(code); - std::vector left = args[0]->pre_compile(code); - code.emplace_back(here, Op::_Let, left, std::move(right)); - return left; - } - default: - std::cerr << "expression constructor is " << cls << std::endl; - throw src::Fatal{"cannot compile expression with unknown constructor"}; - } -} - -/* - * - * PARSE SOURCE - * - */ - -// TE ::= TA | TA -> TE -// TA ::= int | ... | cont | var | _ | () | ( TE { , TE } ) -TypeExpr* parse_type(Lexer& lex); - -TypeExpr* parse_type1(Lexer& lex) { - switch (lex.tp()) { - case _Int: - lex.next(); - return TypeExpr::new_atomic(_Int); - case _Cell: - lex.next(); - return TypeExpr::new_atomic(_Cell); - case _Slice: - lex.next(); - return TypeExpr::new_atomic(_Slice); - case _Builder: - lex.next(); - return TypeExpr::new_atomic(_Builder); - case _Cont: - lex.next(); - return TypeExpr::new_atomic(_Cont); - case _Var: - case '_': - lex.next(); - return TypeExpr::new_hole(); - } - lex.expect('('); - if (lex.tp() == ')') { - lex.next(); - return TypeExpr::new_unit(); - } - auto t1 = parse_type(lex); - if (lex.tp() != ',') { - lex.expect(')'); - return t1; - } - std::vector tlist{1, t1}; - while (lex.tp() == ',') { - lex.next(); - tlist.push_back(parse_type(lex)); - } - lex.expect(')'); - return TypeExpr::new_tensor(std::move(tlist)); -} - -TypeExpr* parse_type(Lexer& lex) { - auto res = parse_type1(lex); - if (lex.tp() == _Mapsto) { - lex.next(); - auto to = parse_type(lex); - return TypeExpr::new_map(res, to); - } else { - return res; - } -} - -FormalArg parse_formal_arg(Lexer& lex, int fa_idx) { - TypeExpr* arg_type = 0; - SrcLocation loc = lex.cur().loc; - if (lex.tp() == '_') { - lex.next(); - if (lex.tp() == ',' || lex.tp() == ')') { - return std::make_tuple(TypeExpr::new_hole(), (SymDef*)nullptr, loc); - } - arg_type = TypeExpr::new_hole(); - loc = lex.cur().loc; - } else if (lex.tp() != _Ident) { - arg_type = parse_type(lex); - } else { - arg_type = TypeExpr::new_hole(); - } - if (lex.tp() == '_' || lex.tp() == ',' || lex.tp() == ')') { - if (lex.tp() == '_') { - loc = lex.cur().loc; - lex.next(); - } - return std::make_tuple(arg_type, (SymDef*)nullptr, loc); - } - if (lex.tp() != _Ident) { - lex.expect(_Ident, "formal parameter name"); - } - loc = lex.cur().loc; - SymDef* new_sym_def = sym::define_symbol(lex.cur().val, true, loc); - if (new_sym_def->value) { - lex.cur().error_at("redefined formal parameter `", "`"); - } - new_sym_def->value = new SymVal{SymVal::_Param, fa_idx, arg_type}; - lex.next(); - return std::make_tuple(arg_type, new_sym_def, loc); -} - -FormalArgList parse_formal_args(Lexer& lex) { - FormalArgList args; - lex.expect('(', "formal argument list"); - if (lex.tp() == ')') { - lex.next(); - return args; - } - int fa_idx = 0; - args.push_back(parse_formal_arg(lex, fa_idx++)); - while (lex.tp() == ',') { - lex.next(); - args.push_back(parse_formal_arg(lex, fa_idx++)); - } - lex.expect(')'); - return args; -} - -TypeExpr* extract_total_arg_type(const FormalArgList& arg_list) { - if (arg_list.empty()) { - return TypeExpr::new_unit(); - } - if (arg_list.size() == 1) { - return std::get<0>(arg_list[0]); - } - std::vector type_list; - for (auto& x : arg_list) { - type_list.push_back(std::get<0>(x)); - } - return TypeExpr::new_tensor(std::move(type_list)); -} - -SymValFunc* make_new_glob_func(SymDef* func_sym, TypeExpr* func_type) { - SymValFunc* res = new SymValFunc{glob_func_cnt, func_type}; - func_sym->value = res; - glob_func.push_back(func_sym); - glob_func_cnt++; - return res; -} - -bool check_global_func(const Lexem& cur, sym_idx_t func_name = 0) { - if (!func_name) { - func_name = cur.val; - } - SymDef* def = sym::lookup_symbol(func_name); - if (!def) { - cur.loc.show_error(std::string{"undefined function `"} + symbols.get_name(func_name) + - "`, defining a global function of unknown type"); - def = sym::define_global_symbol(func_name, 0, cur.loc); - assert(def && "cannot define global function"); - ++undef_func_cnt; - make_new_glob_func(def, TypeExpr::new_hole()); // was: ... ::new_func() - return true; - } - SymVal* val = dynamic_cast(def->value); - if (!val) { - cur.error(std::string{"symbol `"} + symbols.get_name(func_name) + "` has no value and no type"); - return false; - } else if (!val->get_type()) { - cur.error(std::string{"symbol `"} + symbols.get_name(func_name) + "` has no type, possibly not a function"); - return false; - } else { - return true; - } -} - -Expr* parse_expr(Lexer& lex, CodeBlob& code, bool nv = false); - -// parse ( E { , E } ) | () | id | num | _ -Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) { - if (lex.tp() == '(') { - SrcLocation loc{lex.cur().loc}; - lex.next(); - if (lex.tp() == ')') { - lex.next(); - Expr* res = new Expr{Expr::_Tuple, {}}; - res->flags = Expr::_IsRvalue; - res->here = loc; - return res; - } - Expr* res = parse_expr(lex, code, nv); - if (lex.tp() != ',') { - lex.expect(')'); - return res; - } - std::vector type_list; - type_list.push_back(res->e_type); - int f = res->flags; - res = new Expr{Expr::_Tuple, {res}}; - while (lex.tp() == ',') { - lex.next(); - auto x = parse_expr(lex, code, nv); - res->pb_arg(x); - if ((f ^ x->flags) & Expr::_IsType) { - lex.cur().error("mixing type and non-type expressions inside the same tuple"); - } - f &= x->flags; - type_list.push_back(x->e_type); - } - res->here = loc; - res->flags = f; - res->e_type = TypeExpr::new_tensor(std::move(type_list)); - lex.expect(')'); - return res; - } - int t = lex.tp(); - if (t == Lexem::Number) { - Expr* res = new Expr{Expr::_Const, lex.cur().loc}; - res->flags = Expr::_IsRvalue; - td::RefInt256 val{true}; - if (!val.unique_write().parse_dec(lex.cur().str)) { - lex.cur().error_at("invalid integer constant `", "`"); - } - res->intval = std::move(val); - res->e_type = TypeExpr::new_atomic(_Int); - lex.next(); - return res; - } - if (t == '_') { - Expr* res = new Expr{Expr::_Hole, lex.cur().loc}; - res->val = -1; - res->flags = (Expr::_IsLvalue | Expr::_IsHole | Expr::_IsNewVar); - res->e_type = TypeExpr::new_hole(); - lex.next(); - return res; - } - if (t == _Var) { - Expr* res = new Expr{Expr::_Type, lex.cur().loc}; - res->flags = Expr::_IsType; - res->e_type = TypeExpr::new_hole(); - lex.next(); - return res; - } - if (t == _Int || t == _Cell || t == _Slice || t == _Builder || t == _Cont || t == _Type) { - Expr* res = new Expr{Expr::_Type, lex.cur().loc}; - res->flags = Expr::_IsType; - res->e_type = TypeExpr::new_atomic(t); - lex.next(); - return res; - } - if (t == Lexem::Ident) { - Expr* res = new Expr{Expr::_Var, lex.cur().loc}; - if (nv) { - res->val = ~lex.cur().val; - res->e_type = TypeExpr::new_hole(); - res->flags = Expr::_IsLvalue | Expr::_IsNewVar; - // std::cerr << "defined new variable " << lex.cur().str << " : " << res->e_type << std::endl; - } else { - res->sym = sym::lookup_symbol(lex.cur().val); - if (!res->sym) { - check_global_func(lex.cur()); - res->sym = sym::lookup_symbol(lex.cur().val); - } - SymVal* val = nullptr; - if (res->sym) { - val = dynamic_cast(res->sym->value); - } - if (!val) { - lex.cur().error_at("undefined identifier `", "`"); - } else if (val->type == SymVal::_Func) { - res->e_type = val->get_type(); - res->cls = Expr::_Glob; - } else if (val->idx < 0) { - lex.cur().error_at("accessing variable `", "` being defined"); - } else { - res->val = val->idx; - res->e_type = val->get_type(); - // std::cerr << "accessing variable " << lex.cur().str << " : " << res->e_type << std::endl; - } - res->flags = Expr::_IsLvalue | Expr::_IsRvalue; - } - res->deduce_type(lex.cur()); - lex.next(); - return res; - } - lex.expect(Lexem::Ident); - return nullptr; -} - -// parse E { E } -Expr* parse_expr90(Lexer& lex, CodeBlob& code, bool nv) { - Expr* res = parse_expr100(lex, code, nv); - while (lex.tp() == '(' || lex.tp() == _Ident) { - if (res->is_type()) { - Expr* x = parse_expr100(lex, code, true); - x->chk_lvalue(lex.cur()); // chk_lrvalue() ? - TypeExpr* tp = res->e_type; - delete res; - res = new Expr{Expr::_TypeApply, {x}}; - res->e_type = tp; - res->here = lex.cur().loc; - try { - uniformize(res->e_type, x->e_type); - } catch (UniformizeError& ue) { - std::ostringstream os; - os << "cannot transform expression of type " << x->e_type << " to explicitly requested type " << res->e_type - << ": " << ue; - lex.cur().error(os.str()); - } - res->flags = x->flags; - } else { - Expr* x = parse_expr100(lex, code, false); - x->chk_rvalue(lex.cur()); - res = new Expr{Expr::_VarApply, {res, x}}; - res->flags = Expr::_IsRvalue; - res->here = lex.cur().loc; - res->deduce_type(lex.cur()); - } - } - return res; -} - -// parse E { (* | / | % | /% ) E } -Expr* parse_expr30(Lexer& lex, CodeBlob& code, bool nv) { - Expr* res = parse_expr90(lex, code, nv); - while (lex.tp() == '*' || lex.tp() == '/' || lex.tp() == '%' || lex.tp() == _DivMod || lex.tp() == _DivC || - lex.tp() == _DivR || lex.tp() == '&') { - res->chk_rvalue(lex.cur()); - int t = lex.tp(); - sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur().str + "_"); - SrcLocation loc{lex.cur().loc}; - check_global_func(lex.cur(), name); - lex.next(); - auto x = parse_expr90(lex, code, false); - x->chk_rvalue(lex.cur()); - res = new Expr{Expr::_Apply, name, {res, x}}; - res->here = loc; - res->set_val(t); - res->flags = Expr::_IsRvalue; - res->deduce_type(lex.cur()); - } - return res; -} - -// parse [-] E { (+ | - | `|` ) E } -Expr* parse_expr20(Lexer& lex, CodeBlob& code, bool nv) { - Expr* res; - int t = lex.tp(); - if (t == '-') { - sym_idx_t name = symbols.lookup_add("-_"); - check_global_func(lex.cur(), name); - SrcLocation loc{lex.cur().loc}; - lex.next(); - auto x = parse_expr30(lex, code, false); - x->chk_rvalue(lex.cur()); - res = new Expr{Expr::_Apply, name, {x}}; - res->here = loc; - res->set_val(t); - res->flags = Expr::_IsRvalue; - res->deduce_type(lex.cur()); - } else { - res = parse_expr30(lex, code, nv); - } - while (lex.tp() == '-' || lex.tp() == '+' || lex.tp() == '|') { - res->chk_rvalue(lex.cur()); - t = lex.tp(); - sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur().str + "_"); - check_global_func(lex.cur(), name); - SrcLocation loc{lex.cur().loc}; - lex.next(); - auto x = parse_expr30(lex, code, false); - x->chk_rvalue(lex.cur()); - res = new Expr{Expr::_Apply, name, {res, x}}; - res->here = loc; - res->set_val(t); - res->flags = Expr::_IsRvalue; - res->deduce_type(lex.cur()); - } - return res; -} - -// parse E { ( << | >> | >>~ | >>^ ) E } -Expr* parse_expr17(Lexer& lex, CodeBlob& code, bool nv) { - Expr* res = parse_expr20(lex, code, nv); - while (lex.tp() == _Lshift || lex.tp() == _Rshift || lex.tp() == _RshiftC || lex.tp() == _RshiftR) { - res->chk_rvalue(lex.cur()); - int t = lex.tp(); - sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur().str + "_"); - check_global_func(lex.cur(), name); - SrcLocation loc{lex.cur().loc}; - lex.next(); - auto x = parse_expr20(lex, code, false); - x->chk_rvalue(lex.cur()); - res = new Expr{Expr::_Apply, name, {res, x}}; - res->here = loc; - res->set_val(t); - res->flags = Expr::_IsRvalue; - res->deduce_type(lex.cur()); - } - return res; -} - -// parse E [ (== | < | > | <= | >= | != ) E ] -Expr* parse_expr15(Lexer& lex, CodeBlob& code, bool nv) { - Expr* res = parse_expr17(lex, code, nv); - if (lex.tp() == _Eq || lex.tp() == '<' || lex.tp() == '>' || lex.tp() == _Leq || lex.tp() == _Geq || - lex.tp() == _Neq) { - res->chk_rvalue(lex.cur()); - int t = lex.tp(); - sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur().str + "_"); - check_global_func(lex.cur(), name); - SrcLocation loc{lex.cur().loc}; - lex.next(); - auto x = parse_expr17(lex, code, false); - x->chk_rvalue(lex.cur()); - res = new Expr{Expr::_Apply, name, {res, x}}; - res->here = loc; - res->set_val(t); - res->flags = Expr::_IsRvalue; - res->deduce_type(lex.cur()); - } - return res; -} - -// parse LE1 (= | += | -= | ... ) E2 -Expr* parse_expr10(Lexer& lex, CodeBlob& code, bool nv) { - auto x = parse_expr15(lex, code, nv); - int t = lex.tp(); - if (t == _PlusLet || t == _MinusLet || t == _TimesLet || t == _DivLet || t == _DivRLet || t == _DivCLet || - t == _ModLet || t == _LshiftLet || t == _RshiftLet || t == _RshiftCLet || t == _RshiftRLet) { - x->chk_lvalue(lex.cur()); - x->chk_rvalue(lex.cur()); - sym_idx_t name = symbols.lookup_add(std::string{"^_"} + lex.cur().str + "_"); - check_global_func(lex.cur(), name); - SrcLocation loc{lex.cur().loc}; - lex.next(); - auto y = parse_expr10(lex, code, false); - y->chk_rvalue(lex.cur()); - Expr* z = new Expr{Expr::_Apply, name, {x, y}}; - z->here = loc; - z->set_val(t); - z->flags = Expr::_IsRvalue; - z->deduce_type(lex.cur()); - Expr* res = new Expr{Expr::_Letop, {x->copy(), z}}; - res->here = loc; - res->flags = (x->flags & ~Expr::_IsType) | Expr::_IsRvalue; - res->set_val(t); - res->deduce_type(lex.cur()); - return res; - } else if (t == '=') { - x->chk_lvalue(lex.cur()); - SrcLocation loc{lex.cur().loc}; - lex.next(); - auto y = parse_expr10(lex, code, false); - y->chk_rvalue(lex.cur()); - x->predefine_vars(); - x->define_new_vars(code); - Expr* res = new Expr{Expr::_Letop, {x, y}}; - res->here = loc; - res->flags = (x->flags & ~Expr::_IsType) | Expr::_IsRvalue; - res->set_val(t); - res->deduce_type(lex.cur()); - return res; - } else { - return x; - } -} - -Expr* parse_expr(Lexer& lex, CodeBlob& code, bool nv) { - return parse_expr10(lex, code, nv); -} - -namespace blk_fl { -enum { end = 1, ret = 2, empty = 4 }; -typedef int val; -constexpr val init = end | empty; -void combine(val& x, const val y) { - x |= y & ret; - x &= y | ~(end | empty); -} -void combine_parallel(val& x, const val y) { - x &= y | ~(ret | empty); - x |= y & end; -} -} // namespace blk_fl - -blk_fl::val parse_return_stmt(Lexer& lex, CodeBlob& code) { - auto expr = parse_expr(lex, code); - expr->chk_rvalue(lex.cur()); - try { - // std::cerr << "in return: "; - uniformize(expr->e_type, code.ret_type); - } catch (UniformizeError& ue) { - std::ostringstream os; - os << "previous function return type " << code.ret_type - << " cannot be unified with return statement expression type " << expr->e_type << ": " << ue; - lex.cur().error(os.str()); - } - std::vector tmp_vars = expr->pre_compile(code); - code.emplace_back(lex.cur().loc, Op::_Return, std::move(tmp_vars)); - lex.expect(';'); - return blk_fl::ret; -} - -blk_fl::val parse_implicit_ret_stmt(Lexer& lex, CodeBlob& code) { - auto ret_type = TypeExpr::new_unit(); - try { - // std::cerr << "in implicit return: "; - uniformize(ret_type, code.ret_type); - } catch (UniformizeError& ue) { - std::ostringstream os; - os << "previous function return type " << code.ret_type - << " cannot be unified with implicit end-of-block return type " << ret_type << ": " << ue; - lex.cur().error(os.str()); - } - code.emplace_back(lex.cur().loc, Op::_Return); - return blk_fl::ret; -} - -blk_fl::val parse_stmt(Lexer& lex, CodeBlob& code); - -blk_fl::val parse_block_stmt(Lexer& lex, CodeBlob& code) { - lex.expect('{'); - sym::open_scope(lex); - blk_fl::val res = blk_fl::init; - bool warned = false; - while (lex.tp() != '}') { - if (!(res & blk_fl::end) && !warned) { - lex.cur().loc.show_warning("unreachable code"); - warned = true; - } - blk_fl::combine(res, parse_stmt(lex, code)); - } - sym::close_scope(lex); - lex.expect('}'); - return res; -} - -blk_fl::val parse_repeat_stmt(Lexer& lex, CodeBlob& code) { - SrcLocation loc{lex.cur().loc}; - lex.expect(_Repeat); - auto expr = parse_expr(lex, code); - expr->chk_rvalue(lex.cur()); - auto cnt_type = TypeExpr::new_atomic(_Int); - try { - uniformize(expr->e_type, cnt_type); - } catch (UniformizeError& ue) { - std::ostringstream os; - os << "repeat count value of type " << expr->e_type << " is not an integer: " << ue; - lex.cur().error(os.str()); - } - std::vector tmp_vars = expr->pre_compile(code); - if (tmp_vars.size() != 1) { - lex.cur().error("repeat count value is not a singleton"); - } - Op& repeat_op = code.emplace_back(loc, Op::_Repeat, tmp_vars); - code.push_set_cur(repeat_op.block0); - blk_fl::val res = parse_block_stmt(lex, code); - code.close_pop_cur(lex.cur().loc); - return res | blk_fl::end; -} - -blk_fl::val parse_while_stmt(Lexer& lex, CodeBlob& code) { - SrcLocation loc{lex.cur().loc}; - lex.expect(_While); - auto expr = parse_expr(lex, code); - expr->chk_rvalue(lex.cur()); - auto cnt_type = TypeExpr::new_atomic(_Int); - try { - uniformize(expr->e_type, cnt_type); - } catch (UniformizeError& ue) { - std::ostringstream os; - os << "while condition value of type " << expr->e_type << " is not an integer: " << ue; - lex.cur().error(os.str()); - } - Op& while_op = code.emplace_back(loc, Op::_While); - code.push_set_cur(while_op.block0); - while_op.left = expr->pre_compile(code); - code.close_pop_cur(lex.cur().loc); - if (while_op.left.size() != 1) { - lex.cur().error("while condition value is not a singleton"); - } - code.push_set_cur(while_op.block1); - blk_fl::val res1 = parse_block_stmt(lex, code); - code.close_pop_cur(lex.cur().loc); - return res1 | blk_fl::end; -} - -blk_fl::val parse_do_stmt(Lexer& lex, CodeBlob& code) { - Op& while_op = code.emplace_back(lex.cur().loc, Op::_Until); - lex.expect(_Do); - code.push_set_cur(while_op.block0); - blk_fl::val res = parse_block_stmt(lex, code); - lex.expect(_Until); - auto expr = parse_expr(lex, code); - expr->chk_rvalue(lex.cur()); - auto cnt_type = TypeExpr::new_atomic(_Int); - try { - uniformize(expr->e_type, cnt_type); - } catch (UniformizeError& ue) { - std::ostringstream os; - os << "`until` condition value of type " << expr->e_type << " is not an integer: " << ue; - lex.cur().error(os.str()); - } - while_op.left = expr->pre_compile(code); - code.close_pop_cur(lex.cur().loc); - if (while_op.left.size() != 1) { - lex.cur().error("`until` condition value is not a singleton"); - } - return res & ~blk_fl::empty; -} - -blk_fl::val parse_if_stmt(Lexer& lex, CodeBlob& code, int first_lex = _If) { - SrcLocation loc{lex.cur().loc}; - lex.expect(first_lex); - auto expr = parse_expr(lex, code); - expr->chk_rvalue(lex.cur()); - auto flag_type = TypeExpr::new_atomic(_Int); - try { - uniformize(expr->e_type, flag_type); - } catch (UniformizeError& ue) { - std::ostringstream os; - os << "`if` condition value of type " << expr->e_type << " is not an integer: " << ue; - lex.cur().error(os.str()); - } - std::vector tmp_vars = expr->pre_compile(code); - if (tmp_vars.size() != 1) { - lex.cur().error("condition value is not a singleton"); - } - Op& if_op = code.emplace_back(loc, Op::_If, tmp_vars); - code.push_set_cur(if_op.block0); - blk_fl::val res1 = parse_block_stmt(lex, code); - blk_fl::val res2 = blk_fl::init; - code.close_pop_cur(lex.cur().loc); - if (lex.tp() == _Else) { - lex.expect(_Else); - code.push_set_cur(if_op.block1); - res2 = parse_block_stmt(lex, code); - code.close_pop_cur(lex.cur().loc); - } else if (lex.tp() == _Elseif) { - code.push_set_cur(if_op.block1); - res2 = parse_if_stmt(lex, code, _Elseif); - code.close_pop_cur(lex.cur().loc); - } - blk_fl::combine_parallel(res1, res2); - return res1; -} - -blk_fl::val parse_stmt(Lexer& lex, CodeBlob& code) { - switch (lex.tp()) { - case _Return: { - lex.next(); - return parse_return_stmt(lex, code); - } - case '{': { - return parse_block_stmt(lex, code); - } - case ';': { - lex.next(); - return blk_fl::init; - } - case _Repeat: - return parse_repeat_stmt(lex, code); - case _If: - return parse_if_stmt(lex, code); - case _Do: - return parse_do_stmt(lex, code); - case _While: - return parse_while_stmt(lex, code); - default: { - auto expr = parse_expr(lex, code); - expr->chk_rvalue(lex.cur()); - expr->pre_compile(code); - lex.expect(';'); - return blk_fl::end; - } - } -} - -CodeBlob* parse_func_body(Lexer& lex, FormalArgList arg_list, TypeExpr* ret_type) { - lex.expect('{'); - CodeBlob* blob = new CodeBlob{ret_type}; - blob->import_params(std::move(arg_list)); - blk_fl::val res = blk_fl::init; - bool warned = false; - while (lex.tp() != '}') { - if (!(res & blk_fl::end) && !warned) { - lex.cur().loc.show_warning("unreachable code"); - warned = true; - } - blk_fl::combine(res, parse_stmt(lex, *blob)); - } - if (res & blk_fl::end) { - parse_implicit_ret_stmt(lex, *blob); - } - blob->close_blk(lex.cur().loc); - lex.expect('}'); - return blob; -} - -void parse_func_def(Lexer& lex) { - SrcLocation loc{lex.cur().loc}; - sym::open_scope(lex); - auto ret_type = parse_type(lex); - if (lex.tp() != _Ident) { - throw src::ParseError{lex.cur().loc, "function name identifier expected"}; - } - Lexem func_name = lex.cur(); - lex.next(); - FormalArgList arg_list = parse_formal_args(lex); - if (lex.tp() != ';' && lex.tp() != '{') { - lex.expect('{', "function body block expected"); - } - TypeExpr* func_type = TypeExpr::new_map(extract_total_arg_type(arg_list), ret_type); - std::cerr << "function " << func_name.str << " : " << func_type << std::endl; - SymDef* func_sym = sym::define_global_symbol(func_name.val, 0, loc); - assert(func_sym); - SymValFunc* func_sym_val = dynamic_cast(func_sym->value); - if (func_sym->value) { - if (func_sym->value->type != SymVal::_Func || !func_sym_val) { - lex.cur().error("was not defined as a function before"); - } - try { - uniformize(func_sym_val->sym_type, func_type); - } catch (UniformizeError& ue) { - std::ostringstream os; - os << "previous type of function " << func_name.str << " : " << func_sym_val->get_type() - << " cannot be unified with new type " << func_type << ": " << ue; - lex.cur().error(os.str()); - } - } else { - func_sym_val = make_new_glob_func(func_sym, func_type); - } - if (lex.tp() == ';') { - lex.next(); - } else { - if (func_sym_val->code) { - lex.cur().error(std::string{"redefinition of function `"} + func_name.str + "`"); - } - CodeBlob* code = parse_func_body(lex, arg_list, ret_type); - code->name = func_name.str; - code->loc = loc; - code->print(std::cerr); - func_sym_val->code = code; - } - std::cerr << "new type of function " << func_name.str << " : " << func_type << std::endl; - sym::close_scope(lex); -} - -bool parse_source(std::istream* is, const src::FileDescr* fdescr) { - src::SourceReader reader{is, fdescr}; - Lexer lex{reader, true}; - while (lex.tp() != _Eof) { - parse_func_def(lex); - } - return true; -} - -bool parse_source_file(const char* filename) { - if (!filename || !*filename) { - throw src::Fatal{"source file name is an empty string"}; - } - src::FileDescr* cur_source = new src::FileDescr{filename}; - std::ifstream ifs{filename}; - if (ifs.fail()) { - throw src::Fatal{std::string{"cannot open source file `"} + filename + "`"}; - } - return parse_source(&ifs, cur_source); -} - -bool parse_source_stdin() { - return parse_source(&std::cin, new src::FileDescr{"stdin", true}); -} - -/* - * - * ASM-OP LIST FUNCTIONS - * - */ - -void AsmOp::out(std::ostream& os) const { - if (!op.empty()) { - os << op; - return; - } - switch (t) { - case a_none: - break; - case a_xchg: - if (!a && !(b & -2)) { - os << (b ? "SWAP" : "NOP"); - break; - } - os << "s" << a << " s" << b << " XCHG"; - break; - case a_push: - if (!(a & -2)) { - os << (a ? "OVER" : "DUP"); - break; - } - os << "s" << a << " PUSH"; - break; - case a_pop: - if (!(a & -2)) { - os << (a ? "NIP" : "DROP"); - break; - } - os << "s" << a << " POP"; - break; - default: - throw src::Fatal{"unknown assembler operation"}; - } -} - -void AsmOp::out_indent_nl(std::ostream& os, bool no_eol) const { - for (int i = 0; i < indent; i++) { - os << " "; - } - out(os); - if (!no_eol) { - os << std::endl; - } -} - -std::string AsmOp::to_string() const { - if (!op.empty()) { - return op; - } else { - std::ostringstream os; - out(os); - return os.str(); - } -} - -void AsmOpList::show_var(std::ostream& os, var_idx_t idx) const { - if (!var_names_ || (unsigned)idx >= var_names_->size()) { - os << '_' << idx; - } else { - var_names_->at(idx).show(os, 2); - } -} - -void AsmOpList::out(std::ostream& os, int mode) const { - if (!(mode & 2)) { - for (const auto& op : list_) { - os << op; - } - } else { - std::size_t n = list_.size(); - for (std::size_t i = 0; i < n; i++) { - const auto& op = list_[i]; - if (!op.is_comment() && i + 1 < n && list_[i + 1].is_comment()) { - op.out_indent_nl(os, true); - os << '\t'; - do { - i++; - } while (i + 1 < n && list_[i + 1].is_comment()); - list_[i].out(os); - os << std::endl; - } else { - op.out_indent_nl(os, false); - } - } - } -} - -/* - * - * DEFINE BUILT-IN FUNCTIONS - * - */ - -int emulate_negate(int a) { - int f = VarDescr::_Pos | VarDescr::_Neg; - if ((a & f) && (~a & f)) { - a ^= f; - } - f = VarDescr::_Bit | VarDescr::_Bool; - if ((a & f) && (~a & f)) { - a ^= f; - } - return a; -} - -int emulate_add(int a, int b) { - if (b & VarDescr::_Zero) { - return a; - } else if (a & VarDescr::_Zero) { - return b; - } - int u = a & b, v = a | b; - int r = VarDescr::_Int; - int t = u & (VarDescr::_Pos | VarDescr::_Neg); - if (v & VarDescr::_Nan) { - return r | VarDescr::_Nan; - } - // non-quiet addition always returns finite results! - r |= t | VarDescr::_Finite; - if (t) { - r |= v & VarDescr::_NonZero; - } - r |= v & VarDescr::_Nan; - if (u & (VarDescr::_Odd | VarDescr::_Even)) { - r |= VarDescr::_Even; - } else if (!(~v & (VarDescr::_Odd | VarDescr::_Even))) { - r |= VarDescr::_Odd | VarDescr::_NonZero; - } - return r; -} - -int emulate_sub(int a, int b) { - return emulate_add(a, emulate_negate(b)); -} - -int emulate_mul(int a, int b) { - if ((b & (VarDescr::_NonZero | VarDescr::_Bit)) == (VarDescr::_NonZero | VarDescr::_Bit)) { - return a; - } else if ((a & (VarDescr::_NonZero | VarDescr::_Bit)) == (VarDescr::_NonZero | VarDescr::_Bit)) { - return b; - } - int u = a & b, v = a | b; - int r = VarDescr::_Int; - if (v & VarDescr::_Nan) { - return r | VarDescr::_Nan; - } - // non-quiet multiplication always yields finite results, if any - r |= VarDescr::_Finite; - if (v & VarDescr::_Zero) { - // non-quiet multiplication - // the result is zero, if any result at all - return VarDescr::ConstZero; - } - if (u & (VarDescr::_Pos | VarDescr::_Neg)) { - r |= VarDescr::_Pos; - } else if (!(~v & (VarDescr::_Pos | VarDescr::_Neg))) { - r |= VarDescr::_Neg; - } - if (u & (VarDescr::_Bit | VarDescr::_Bool)) { - r |= VarDescr::_Bit; - } else if (!(~v & (VarDescr::_Bit | VarDescr::_Bool))) { - r |= VarDescr::_Bool; - } - r |= v & VarDescr::_Even; - r |= u & (VarDescr::_Odd | VarDescr::_NonZero); - return r; -} - -int emulate_lshift(int a, int b) { - if (((a | b) & VarDescr::_Nan) || !(~b & (VarDescr::_Neg | VarDescr::_NonZero))) { - return VarDescr::_Int | VarDescr::_Nan; - } - if (b & VarDescr::_Zero) { - return a; - } - int t = ((b & VarDescr::_NonZero) ? VarDescr::_Even : 0); - t |= b & VarDescr::_Finite; - return emulate_mul(a, VarDescr::_Int | VarDescr::_Pos | VarDescr::_NonZero | VarDescr::_Even | t); -} - -int emulate_div(int a, int b) { - if ((b & (VarDescr::_NonZero | VarDescr::_Bit)) == (VarDescr::_NonZero | VarDescr::_Bit)) { - return a; - } else if ((b & (VarDescr::_NonZero | VarDescr::_Bool)) == (VarDescr::_NonZero | VarDescr::_Bool)) { - return emulate_negate(b); - } - if (b & VarDescr::_Zero) { - return VarDescr::_Int | VarDescr::_Nan; - } - int u = a & b, v = a | b; - int r = VarDescr::_Int; - if (v & VarDescr::_Nan) { - return r | VarDescr::_Nan; - } - // non-quiet division always yields finite results, if any - r |= VarDescr::_Finite; - if (a & VarDescr::_Zero) { - // non-quiet division - // the result is zero, if any result at all - return VarDescr::ConstZero; - } - if (u & (VarDescr::_Pos | VarDescr::_Neg)) { - r |= VarDescr::_Pos; - } else if (!(~v & (VarDescr::_Pos | VarDescr::_Neg))) { - r |= VarDescr::_Neg; - } - if (u & (VarDescr::_Bit | VarDescr::_Bool)) { - r |= VarDescr::_Bit; - } else if (!(~v & (VarDescr::_Bit | VarDescr::_Bool))) { - r |= VarDescr::_Bool; - } - return r; -} - -int emulate_rshift(int a, int b) { - if (((a | b) & VarDescr::_Nan) || !(~b & (VarDescr::_Neg | VarDescr::_NonZero))) { - return VarDescr::_Int | VarDescr::_Nan; - } - if (b & VarDescr::_Zero) { - return a; - } - int t = ((b & VarDescr::_NonZero) ? VarDescr::_Even : 0); - t |= b & VarDescr::_Finite; - return emulate_div(a, VarDescr::_Int | VarDescr::_Pos | VarDescr::_NonZero | VarDescr::_Even | t); -} - -int emulate_mod(int a, int b, int round_mode = -1) { - if ((b & (VarDescr::_NonZero | VarDescr::_Bit)) == (VarDescr::_NonZero | VarDescr::_Bit)) { - return VarDescr::ConstZero; - } else if ((b & (VarDescr::_NonZero | VarDescr::_Bool)) == (VarDescr::_NonZero | VarDescr::_Bool)) { - return VarDescr::ConstZero; - } - if (b & VarDescr::_Zero) { - return VarDescr::_Int | VarDescr::_Nan; - } - int r = VarDescr::_Int; - if ((a | b) & VarDescr::_Nan) { - return r | VarDescr::_Nan; - } - // non-quiet division always yields finite results, if any - r |= VarDescr::_Finite; - if (a & VarDescr::_Zero) { - // non-quiet division - // the result is zero, if any result at all - return VarDescr::ConstZero; - } - if (round_mode < 0) { - r |= b & (VarDescr::_Pos | VarDescr::_Neg); - } else if (round_mode > 0) { - r |= emulate_negate(b) & (VarDescr::_Pos | VarDescr::_Neg); - } - if (a & (VarDescr::_Bit | VarDescr::_Bool)) { - if (r & VarDescr::_Pos) { - r |= VarDescr::_Bit; - } - if (r & VarDescr::_Neg) { - r |= VarDescr::_Bool; - } - } - if (b & VarDescr::_Even) { - r |= a & (VarDescr::_Even | VarDescr::_Odd); - } - return r; -} - -int is_pos_pow2(td::RefInt256 x) { - if (sgn(x) > 0 && !sgn(x & (x - 1))) { - return x->bit_size() - 1; - } else { - return -1; - } -} - -int is_neg_pow2(td::RefInt256 x) { - return sgn(x) < 0 ? is_pos_pow2(-x) : 0; -} - -AsmOp exec_op(std::string op) { - return AsmOp::Custom(op); -} - -AsmOp exec_op(std::string op, int args, int retv = 1) { - return AsmOp::Custom(op, args, retv); -} - -AsmOp exec_arg_op(std::string op, long long arg) { - std::ostringstream os; - os << arg << ' ' << op; - return AsmOp::Custom(os.str()); -} - -AsmOp exec_arg_op(std::string op, long long arg, int args, int retv = 1) { - std::ostringstream os; - os << arg << ' ' << op; - return AsmOp::Custom(os.str(), args, retv); -} - -AsmOp exec_arg_op(std::string op, td::RefInt256 arg) { - std::ostringstream os; - os << arg << ' ' << op; - return AsmOp::Custom(os.str()); -} - -AsmOp exec_arg_op(std::string op, td::RefInt256 arg, int args, int retv = 1) { - std::ostringstream os; - os << arg << ' ' << op; - return AsmOp::Custom(os.str(), args, retv); -} - -AsmOp AsmOp::Const(int arg, std::string push_op) { - std::ostringstream os; - os << arg << ' ' << push_op; - return AsmOp::Custom(os.str()); -} - -AsmOp push_const(td::RefInt256 x) { - return AsmOp::IntConst(std::move(x)); -} - -AsmOp AsmOp::IntConst(td::RefInt256 x) { - if (x->signed_fits_bits(8)) { - return AsmOp::Const(dec_string(std::move(x)) + " PUSHINT"); - } - if (!x->is_valid()) { - return AsmOp::Const("PUSHNAN"); - } - int k = is_pos_pow2(x); - if (k >= 0) { - return AsmOp::Const(k, "PUSHPOW2"); - } - k = is_pos_pow2(x + 1); - if (k >= 0) { - return AsmOp::Const(k, "PUSHPOW2DEC"); - } - k = is_pos_pow2(-x); - if (k >= 0) { - return AsmOp::Const(k, "PUSHNEGPOW2"); - } - return AsmOp::Const(dec_string(std::move(x)) + " PUSHINT"); -} - -AsmOp compile_add(std::vector& res, std::vector& args) { - assert(res.size() == 1 && args.size() == 2); - VarDescr &r = res[0], &x = args[0], &y = args[1]; - if (x.is_int_const() && y.is_int_const()) { - r.set_const(x.int_const + y.int_const); - x.unused(); - y.unused(); - return push_const(r.int_const); - } - r.val = emulate_add(x.val, y.val); - if (y.is_int_const() && y.int_const->signed_fits_bits(8)) { - y.unused(); - if (y.always_zero()) { - return AsmOp::Nop(); - } - if (*y.int_const == 1) { - return exec_op("INC", 1); - } - if (*y.int_const == -1) { - return exec_op("DEC", 1); - } - return exec_arg_op("ADDCONST", y.int_const, 1); - } - if (x.is_int_const() && x.int_const->signed_fits_bits(8)) { - x.unused(); - if (x.always_zero()) { - return AsmOp::Nop(); - } - if (*x.int_const == 1) { - return exec_op("INC", 1); - } - if (*x.int_const == -1) { - return exec_op("DEC", 1); - } - return exec_arg_op("ADDCONST", x.int_const, 1); - } - return exec_op("ADD", 2); -} - -AsmOp compile_sub(std::vector& res, std::vector& args) { - assert(res.size() == 1 && args.size() == 2); - VarDescr &r = res[0], &x = args[0], &y = args[1]; - if (x.is_int_const() && y.is_int_const()) { - r.set_const(x.int_const - y.int_const); - x.unused(); - y.unused(); - return push_const(r.int_const); - } - r.val = emulate_sub(x.val, y.val); - if (y.is_int_const() && (-y.int_const)->signed_fits_bits(8)) { - y.unused(); - if (y.always_zero()) { - return {}; - } - if (*y.int_const == 1) { - return exec_op("DEC", 1); - } - if (*y.int_const == -1) { - return exec_op("INC", 1); - } - return exec_arg_op("ADDCONST", -y.int_const, 1); - } - if (x.always_zero()) { - x.unused(); - return exec_op("NEGATE", 1); - } - return exec_op("SUB", 2); -} - -AsmOp compile_negate(std::vector& res, std::vector& args) { - assert(res.size() == 1 && args.size() == 1); - VarDescr &r = res[0], &x = args[0]; - if (x.is_int_const()) { - r.set_const(-x.int_const); - x.unused(); - return push_const(r.int_const); - } - r.val = emulate_negate(x.val); - return exec_op("NEGATE", 1); -} - -AsmOp compile_mul(std::vector& res, std::vector& args) { - assert(res.size() == 1 && args.size() == 2); - VarDescr &r = res[0], &x = args[0], &y = args[1]; - if (x.is_int_const() && y.is_int_const()) { - r.set_const(x.int_const * y.int_const); - x.unused(); - y.unused(); - return push_const(r.int_const); - } - r.val = emulate_mul(x.val, y.val); - if (y.is_int_const()) { - int k = is_pos_pow2(y.int_const); - if (y.int_const->signed_fits_bits(8) && k < 0) { - y.unused(); - if (y.always_zero() && x.always_finite()) { - // dubious optimization: NaN * 0 = ? - r.set_const(y.int_const); - return push_const(r.int_const); - } - if (*y.int_const == 1 && x.always_finite()) { - return AsmOp::Nop(); - } - if (*y.int_const == -1) { - return exec_op("NEGATE", 1); - } - return exec_arg_op("MULCONST", y.int_const, 1); - } - if (k >= 0) { - y.unused(); - return exec_arg_op("LSHIFT#", k, 1); - } - } - if (x.is_int_const()) { - int k = is_pos_pow2(x.int_const); - if (x.int_const->signed_fits_bits(8) && k < 0) { - x.unused(); - if (x.always_zero() && y.always_finite()) { - // dubious optimization: NaN * 0 = ? - r.set_const(x.int_const); - return push_const(r.int_const); - } - if (*x.int_const == 1 && y.always_finite()) { - return AsmOp::Nop(); - } - if (*x.int_const == -1) { - return exec_op("NEGATE", 1); - } - return exec_arg_op("MULCONST", x.int_const, 1); - } - if (k >= 0) { - x.unused(); - return exec_arg_op("LSHIFT#", k, 1); - } - } - return exec_op("MUL", 2); -} - -AsmOp compile_lshift(std::vector& res, std::vector& args) { - assert(res.size() == 1 && args.size() == 2); - VarDescr &r = res[0], &x = args[0], &y = args[1]; - if (y.is_int_const()) { - auto yv = y.int_const->to_long(); - if (yv < 0 || yv > 256) { - r.set_const_nan(); - x.unused(); - y.unused(); - return push_const(r.int_const); - } else if (x.is_int_const()) { - r.set_const(x.int_const << (int)yv); - x.unused(); - y.unused(); - return push_const(r.int_const); - } - } - r.val = emulate_lshift(x.val, y.val); - if (y.is_int_const()) { - int k = (int)(y.int_const->to_long()); - if (!k /* && x.always_finite() */) { - // dubious optimization: what if x=NaN ? - y.unused(); - return AsmOp::Nop(); - } - y.unused(); - return exec_arg_op("LSHIFT#", k, 1); - } - if (x.is_int_const()) { - auto xv = x.int_const->to_long(); - if (xv == 1) { - x.unused(); - return exec_op("POW2", 1); - } - if (xv == -1) { - x.unused(); - return exec_op("NEGPOW2", 1); - } - } - return exec_op("LSHIFT", 2); -} - -AsmOp compile_rshift(std::vector& res, std::vector& args, int round_mode) { - assert(res.size() == 1 && args.size() == 2); - VarDescr &r = res[0], &x = args[0], &y = args[1]; - if (y.is_int_const()) { - auto yv = y.int_const->to_long(); - if (yv < 0 || yv > 256) { - r.set_const_nan(); - x.unused(); - y.unused(); - return push_const(r.int_const); - } else if (x.is_int_const()) { - r.set_const(td::rshift(x.int_const, (int)yv, round_mode)); - x.unused(); - y.unused(); - return push_const(r.int_const); - } - } - r.val = emulate_rshift(x.val, y.val); - std::string rshift = (round_mode < 0 ? "RSHIFT" : (round_mode ? "RSHIFTC" : "RSHIFTR")); - if (y.is_int_const()) { - int k = (int)(y.int_const->to_long()); - if (!k /* && x.always_finite() */) { - // dubious optimization: what if x=NaN ? - y.unused(); - return AsmOp::Nop(); - } - y.unused(); - return exec_arg_op(rshift + "#", k, 1); - } - return exec_op(rshift, 2); -} - -AsmOp compile_div(std::vector& res, std::vector& args, int round_mode) { - assert(res.size() == 1 && args.size() == 2); - VarDescr &r = res[0], &x = args[0], &y = args[1]; - if (x.is_int_const() && y.is_int_const()) { - r.set_const(div(x.int_const, y.int_const, round_mode)); - x.unused(); - y.unused(); - return push_const(r.int_const); - } - r.val = emulate_div(x.val, y.val); - if (y.is_int_const()) { - if (*y.int_const == 0) { - x.unused(); - y.unused(); - r.set_const(div(y.int_const, y.int_const)); - return push_const(r.int_const); - } - if (*y.int_const == 1 && x.always_finite()) { - y.unused(); - return AsmOp::Nop(); - } - if (*y.int_const == -1) { - y.unused(); - return exec_op("NEGATE", 1); - } - int k = is_pos_pow2(y.int_const); - if (k > 0) { - y.unused(); - std::string op = "RSHIFT"; - if (round_mode >= 0) { - op += (round_mode > 0 ? 'C' : 'R'); - } - return exec_arg_op(op + '#', k, 1); - } - } - std::string op = "DIV"; - if (round_mode >= 0) { - op += (round_mode > 0 ? 'C' : 'R'); - } - return exec_op(op, 2); -} - -AsmOp compile_mod(std::vector& res, std::vector& args, int round_mode) { - assert(res.size() == 1 && args.size() == 2); - VarDescr &r = res[0], &x = args[0], &y = args[1]; - if (x.is_int_const() && y.is_int_const()) { - r.set_const(mod(x.int_const, y.int_const, round_mode)); - x.unused(); - y.unused(); - return push_const(r.int_const); - } - r.val = emulate_mod(x.val, y.val); - if (y.is_int_const()) { - if (*y.int_const == 0) { - x.unused(); - y.unused(); - r.set_const(mod(y.int_const, y.int_const)); - return push_const(r.int_const); - } - if ((*y.int_const == 1 || *y.int_const == -1) && x.always_finite()) { - x.unused(); - y.unused(); - r.set_const(td::RefInt256{true, 0}); - return push_const(r.int_const); - } - int k = is_pos_pow2(y.int_const); - if (k > 0) { - y.unused(); - std::string op = "MODPOW2"; - if (round_mode >= 0) { - op += (round_mode > 0 ? 'C' : 'R'); - } - return exec_arg_op(op + '#', k, 1); - } - } - std::string op = "MOD"; - if (round_mode >= 0) { - op += (round_mode > 0 ? 'C' : 'R'); - } - return exec_op(op, 2); -} - -void define_builtins() { - using namespace std::placeholders; - auto Int = TypeExpr::new_atomic(_Int); - auto Int2 = TypeExpr::new_tensor({Int, Int}); - auto Int3 = TypeExpr::new_tensor({Int, Int, Int}); - auto arith_bin_op = TypeExpr::new_map(Int2, Int); - auto arith_un_op = TypeExpr::new_map(Int, Int); - //auto arith_null_op = TypeExpr::new_map(TypeExpr::new_unit(), Int); - define_builtin_func("_+_", arith_bin_op, compile_add); - define_builtin_func("_-_", arith_bin_op, compile_sub); - define_builtin_func("-_", arith_un_op, compile_negate); - define_builtin_func("_*_", arith_bin_op, compile_mul); - define_builtin_func("_/_", arith_bin_op, std::bind(compile_div, _1, _2, -1)); - define_builtin_func("_/~_", arith_bin_op, std::bind(compile_div, _1, _2, 0)); - define_builtin_func("_/^_", arith_bin_op, std::bind(compile_div, _1, _2, 1)); - define_builtin_func("_%_", arith_bin_op, std::bind(compile_mod, _1, _2, -1)); - define_builtin_func("_%~_", arith_bin_op, std::bind(compile_mod, _1, _2, 0)); - define_builtin_func("_%^_", arith_bin_op, std::bind(compile_mod, _1, _2, -1)); - define_builtin_func("_/%_", TypeExpr::new_map(Int2, Int2), AsmOp::Custom("DIVMOD", 2, 2)); - define_builtin_func("_<<_", arith_bin_op, compile_lshift); - define_builtin_func("_>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, -1)); - define_builtin_func("_>>~_", arith_bin_op, std::bind(compile_rshift, _1, _2, 0)); - define_builtin_func("_>>^_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1)); - define_builtin_func("_&_", arith_bin_op, AsmOp::Custom("AND", 2)); - define_builtin_func("_|_", arith_bin_op, AsmOp::Custom("OR", 2)); - define_builtin_func("_^_", arith_bin_op, AsmOp::Custom("XOR", 2)); - define_builtin_func("^_+=_", arith_bin_op, compile_add); - define_builtin_func("^_-=_", arith_bin_op, compile_sub); - define_builtin_func("^_*=_", arith_bin_op, compile_mul); - define_builtin_func("^_/=_", arith_bin_op, std::bind(compile_div, _1, _2, -1)); - define_builtin_func("^_/~=_", arith_bin_op, std::bind(compile_div, _1, _2, 0)); - define_builtin_func("^_/^=_", arith_bin_op, std::bind(compile_div, _1, _2, 1)); - define_builtin_func("^_%=_", arith_bin_op, std::bind(compile_mod, _1, _2, -1)); - define_builtin_func("^_%~=_", arith_bin_op, std::bind(compile_mod, _1, _2, 0)); - define_builtin_func("^_%^=_", arith_bin_op, std::bind(compile_mod, _1, _2, 1)); - define_builtin_func("^_<<=_", arith_bin_op, compile_lshift); - define_builtin_func("^_>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, -1)); - define_builtin_func("^_>>~=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 0)); - define_builtin_func("^_>>^=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1)); - define_builtin_func("^_&=_", arith_bin_op, AsmOp::Custom("AND", 2)); - define_builtin_func("^_|=_", arith_bin_op, AsmOp::Custom("OR", 2)); - define_builtin_func("^_^=_", arith_bin_op, AsmOp::Custom("XOR", 2)); - define_builtin_func("muldivr", TypeExpr::new_map(Int3, Int), AsmOp::Custom("MULDIVR", 3)); - define_builtin_func("muldiv", TypeExpr::new_map(Int3, Int), AsmOp::Custom("MULDIV", 3)); - define_builtin_func("muldivmod", TypeExpr::new_map(Int3, Int2), AsmOp::Custom("MULDIVMOD", 3, 2)); - define_builtin_func("_==_", arith_bin_op, AsmOp::Custom("EQ", 2)); - define_builtin_func("_!=_", arith_bin_op, AsmOp::Custom("NEQ", 2)); - define_builtin_func("_<_", arith_bin_op, AsmOp::Custom("LESS", 2)); - define_builtin_func("_>_", arith_bin_op, AsmOp::Custom("GREATER", 2)); - define_builtin_func("_<=_", arith_bin_op, AsmOp::Custom("LEQ", 2)); - define_builtin_func("_>=_", arith_bin_op, AsmOp::Custom("GEQ", 2)); - define_builtin_func("true", Int, AsmOp::Const("TRUE")); - define_builtin_func("false", Int, AsmOp::Const("FALSE")); -} - -/* - * - * GENERATE OUTPUT SOURCE CODE - * - */ - -int CodeBlob::split_vars(bool strict) { - int n = var_cnt, changes = 0; - for (int j = 0; j < var_cnt; j++) { - TmpVar& var = vars[j]; - if (strict && var.v_type->minw != var.v_type->maxw) { - throw src::ParseError{var.where.get(), "variable does not have fixed width, cannot manipulate it"}; - } - std::vector comp_types; - int k = var.v_type->extract_components(comp_types); - assert(k <= 254 && n <= 0x7fff00); - assert((unsigned)k == comp_types.size()); - if (k != 1) { - var.coord = ~((n << 8) + k); - for (int i = 0; i < k; i++) { - auto v = create_var(vars[j].cls, comp_types[i], 0, vars[j].where.get()); - assert(v == n + i); - assert(vars[v].idx == v); - vars[v].name = vars[j].name; - vars[v].coord = ((int)j << 8) + i + 1; - } - n += k; - ++changes; - } else if (strict && var.v_type->minw != 1) { - throw src::ParseError{var.where.get(), - "cannot work with variable or variable component of width greater than one"}; - } - } - if (!changes) { - return 0; - } - for (auto& op : ops) { - op.split_vars(vars); - } - return changes; -} - -bool CodeBlob::compute_used_code_vars() { - VarDescrList empty_var_info; - return compute_used_code_vars(ops, empty_var_info, true); -} - -bool CodeBlob::compute_used_code_vars(std::unique_ptr& ops_ptr, const VarDescrList& var_info, bool edit) const { - assert(ops_ptr); - if (!ops_ptr->next) { - assert(ops_ptr->cl == Op::_Nop); - return ops_ptr->set_var_info(var_info); - } - return compute_used_code_vars(ops_ptr->next, var_info, edit) | ops_ptr->compute_used_vars(*this, edit); -} - -bool operator==(const VarDescrList& x, const VarDescrList& y) { - if (x.size() != y.size()) { - return false; - } - for (std::size_t i = 0; i < x.size(); i++) { - if (x.list[i].idx != y.list[i].idx || x.list[i].flags != y.list[i].flags) { - return false; - } - } - return true; -} - -bool same_values(const VarDescr& x, const VarDescr& y) { - if (x.val != y.val || x.int_const.is_null() != y.int_const.is_null()) { - return false; - } - if (x.int_const.not_null() && cmp(x.int_const, y.int_const) != 0) { - return false; - } - return true; -} - -bool same_values(const VarDescrList& x, const VarDescrList& y) { - if (x.size() != y.size()) { - return false; - } - for (std::size_t i = 0; i < x.size(); i++) { - if (x.list[i].idx != y.list[i].idx || !same_values(x.list[i], y.list[i])) { - return false; - } - } - return true; -} - -bool Op::set_var_info(const VarDescrList& new_var_info) { - if (var_info == new_var_info) { - return false; - } - var_info = new_var_info; - return true; -} - -bool Op::set_var_info(VarDescrList&& new_var_info) { - if (var_info == new_var_info) { - return false; - } - var_info = std::move(new_var_info); - return true; -} - -std::vector sort_unique_vars(const std::vector& var_list) { - std::vector vars{var_list}, unique_vars; - std::sort(vars.begin(), vars.end()); - vars.erase(std::unique(vars.begin(), vars.end()), vars.end()); - return vars; -} - -VarDescr* VarDescrList::operator[](var_idx_t idx) { - for (auto& var : list) { - if (var.idx == idx) { - return &var; - } - } - return nullptr; -} - -const VarDescr* VarDescrList::operator[](var_idx_t idx) const { - for (auto& var : list) { - if (var.idx == idx) { - return &var; - } - } - return nullptr; -} - -std::size_t VarDescrList::count(const std::vector idx_list) const { - std::size_t res = 0; - for (var_idx_t idx : idx_list) { - if (operator[](idx)) { - ++res; - } - } - return res; -} - -VarDescrList& VarDescrList::operator-=(var_idx_t idx) { - for (auto it = list.begin(); it != list.end(); ++it) { - if (it->idx == idx) { - list.erase(it); - break; - } - } - return *this; -} - -VarDescrList& VarDescrList::operator-=(const std::vector& idx_list) { - for (var_idx_t idx : idx_list) { - *this -= idx; - } - return *this; -} - -VarDescrList& VarDescrList::operator+=(var_idx_t idx) { - auto it = list.begin(); - for (; it != list.end(); ++it) { - if (it->idx == idx) { - return *this; - } else if (it->idx > idx) { - break; - } - } - list.emplace(it, idx, VarDescr::_Last); - return *this; -} - -VarDescrList& VarDescrList::operator+=(const std::vector& idx_list) { - for (var_idx_t idx : idx_list) { - *this += idx; - } - return *this; -} - -VarDescrList& VarDescrList::clear_last() { - for (auto& var : list) { - if (var.flags & VarDescr::_Last) { - var.flags &= ~VarDescr::_Last; - } - } - return *this; -} - -VarDescrList VarDescrList::operator+(const VarDescrList& y) const { - VarDescrList res; - auto it1 = list.cbegin(); - auto it2 = y.list.cbegin(); - while (it1 != list.cend() && it2 != y.list.cend()) { - if (it1->idx < it2->idx) { - res.list.push_back(*it1++); - } else if (it1->idx > it2->idx) { - res.list.push_back(*it2++); - } else { - res.list.push_back(*it1++); - it2++; - } - } - while (it1 != list.cend()) { - res.list.push_back(*it1++); - } - while (it2 != y.list.cend()) { - res.list.push_back(*it2++); - } - return res; -} - -VarDescrList& VarDescrList::operator+=(const VarDescrList& y) { - return *this = *this + y; -} - -VarDescrList VarDescrList::operator|(const VarDescrList& y) const { - VarDescrList res; - auto it1 = list.cbegin(); - auto it2 = y.list.cbegin(); - while (it1 != list.cend() && it2 != y.list.cend()) { - if (it1->idx < it2->idx) { - it1++; - } else if (it1->idx > it2->idx) { - it2++; - } else { - res.list.push_back(*it1++); - res.list.back() |= *it2++; - } - } - return res; -} - -VarDescrList& VarDescrList::operator|=(const VarDescrList& y) { - return *this = *this | y; -} - -VarDescr& VarDescrList::add(var_idx_t idx) { - auto it = list.begin(); - std::size_t k = 0; - for (; it != list.end(); ++it, ++k) { - if (it->idx == idx) { - return *it; - } else if (it->idx > idx) { - break; - } - } - list.emplace(it, idx); - return list[k]; -} - -VarDescr& VarDescrList::add_newval(var_idx_t idx) { - auto it = list.begin(); - std::size_t k = 0; - for (; it != list.end(); ++it, ++k) { - if (it->idx == idx) { - it->clear_value(); - return *it; - } else if (it->idx > idx) { - break; - } - } - list.emplace(it, idx); - return list[k]; -} - -VarDescrList& VarDescrList::operator&=(const VarDescrList& values) { - for (const VarDescr& vd : values.list) { - VarDescr* item = operator[](vd.idx); - if (item) { - *item &= vd; - } - } - return *this; -} - -VarDescrList& VarDescrList::import_values(const VarDescrList& values) { - for (const VarDescr& vd : values.list) { - VarDescr* item = operator[](vd.idx); - if (item) { - item->set_value(vd); - } - } - return *this; -} - -bool Op::std_compute_used_vars() { - // left = OP right - // var_info := (var_info - left) + right - VarDescrList new_var_info{next->var_info}; - new_var_info -= left; - new_var_info.clear_last(); - if (args.size() == right.size()) { - for (const VarDescr& arg : args) { - if (!arg.is_unused()) { - new_var_info += arg.idx; - } - } - } else { - new_var_info += right; - } - return set_var_info(std::move(new_var_info)); -} - -bool Op::compute_used_vars(const CodeBlob& code, bool edit) { - assert(next); - const VarDescrList& next_var_info = next->var_info; - if (disabled() || cl == _Nop) { - return set_var_info(next_var_info); - } - switch (cl) { - case _IntConst: - case _GlobVar: - case _Call: - case _CallInd: { - // left = EXEC right; - if (!next_var_info.count(left)) { - // all variables in `left` are not needed - if (edit) { - disable(); - } - set_var_info(next_var_info); - return true; - } - return std_compute_used_vars(); - } - case _Let: { - // left = right - std::size_t cnt = next_var_info.count(left); - bool changes = false; - if (!cnt) { - // all variables in `left` are not needed - if (edit) { - disable(); - } - set_var_info(next_var_info); - return true; - } else if (cnt < left.size()) { - assert(left.size() == right.size()); - std::vector new_left, new_right; - auto l_it = left.cbegin(), r_it = right.cbegin(); - for (; l_it < left.cend(); ++l_it, ++r_it) { - if (std::find(l_it + 1, left.cend(), *l_it) == left.cend() && next_var_info[*l_it]) { - new_left.push_back(*l_it); - new_right.push_back(*r_it); - } - } - if (edit) { - left = std::move(new_left); - right = std::move(new_right); - changes = true; - } else { - VarDescrList new_var_info{next_var_info}; - new_var_info -= new_left; - new_var_info.clear_last(); - new_var_info += new_right; - return set_var_info(std::move(new_var_info)); - } - } - return std_compute_used_vars() | changes; - } - case _Return: { - // return left - if (var_info.count(left) == left.size()) { - return false; - } - std::vector unique_vars = sort_unique_vars(left); - var_info.list.clear(); - for (var_idx_t i : unique_vars) { - var_info.list.emplace_back(i, VarDescr::_Last); - } - return true; - } - case _Import: { - // import left - std::vector unique_vars = sort_unique_vars(left); - var_info.list.clear(); - for (var_idx_t i : unique_vars) { - var_info.list.emplace_back(i, next_var_info[i] ? 0 : VarDescr::_Last); - } - return true; - } - case _If: { - // if (left) then block0 else block1 - code.compute_used_code_vars(block0, next_var_info, edit); - code.compute_used_code_vars(block1, next_var_info, edit); - VarDescrList merge_info = block0->var_info + block1->var_info; - merge_info.clear_last(); - merge_info += left; - return set_var_info(std::move(merge_info)); - } - case _While: { - // while (block0 || left) block1; - // ... { block0 left block1 } block0 left next - VarDescrList after_cond_first{next_var_info}; - after_cond_first += left; - code.compute_used_code_vars(block0, after_cond_first, false); - VarDescrList new_var_info{block0->var_info}; - bool changes = false; - do { - code.compute_used_code_vars(block1, block0->var_info, changes); - VarDescrList after_cond{block1->var_info}; - after_cond += left; - code.compute_used_code_vars(block0, after_cond, changes); - std::size_t n = new_var_info.size(); - new_var_info += block0->var_info; - new_var_info.clear_last(); - if (changes) { - break; - } - changes = (new_var_info.size() == n); - } while (changes <= edit); - return set_var_info(std::move(new_var_info)); - } - case _Until: { - // until (block0 || left); - // .. { block0 left } block0 left next - VarDescrList after_cond_first{next_var_info}; - after_cond_first += left; - code.compute_used_code_vars(block0, after_cond_first, false); - VarDescrList new_var_info{block0->var_info}; - bool changes = false; - do { - VarDescrList after_cond{new_var_info}; - after_cond += next_var_info; - after_cond += left; - code.compute_used_code_vars(block0, after_cond, changes); - std::size_t n = new_var_info.size(); - new_var_info += block0->var_info; - new_var_info.clear_last(); - if (changes) { - break; - } - changes = (new_var_info.size() == n); - } while (changes <= edit); - return set_var_info(std::move(new_var_info) + next_var_info); - } - case _Repeat: { - // repeat (left) block0 - // left { block0 } next - VarDescrList new_var_info{next_var_info}; - bool changes = false; - do { - code.compute_used_code_vars(block0, new_var_info, changes); - std::size_t n = new_var_info.size(); - new_var_info += block0->var_info; - new_var_info.clear_last(); - if (changes) { - break; - } - changes = (new_var_info.size() == n); - } while (changes <= edit); - new_var_info += left; - return set_var_info(std::move(new_var_info)); - } - case _Again: { - // for(;;) block0 - // { block0 } - VarDescrList new_var_info; - bool changes = false; - do { - code.compute_used_code_vars(block0, new_var_info, changes); - std::size_t n = new_var_info.size(); - new_var_info += block0->var_info; - new_var_info.clear_last(); - if (changes) { - break; - } - changes = (new_var_info.size() == n); - } while (changes <= edit); - return set_var_info(std::move(new_var_info)); - } - default: - std::cerr << "fatal: unknown operation \n"; - throw src::ParseError{where, "unknown operation"}; - } -} - -bool prune_unreachable(std::unique_ptr& ops) { - if (!ops) { - return true; - } - Op& op = *ops; - if (op.disabled() || op.cl == Op::_Nop) { - if (op.next) { - ops = std::move(op.next); - return prune_unreachable(ops); - } - return true; - } - bool reach; - switch (op.cl) { - case Op::_IntConst: - case Op::_GlobVar: - case Op::_Call: - case Op::_CallInd: - case Op::_Import: - reach = true; - break; - case Op::_Let: { - reach = true; - break; - } - case Op::_Return: - reach = false; - break; - case Op::_If: { - // if left then block0 else block1; ... - VarDescr* c_var = op.var_info[op.left[0]]; - if (c_var && c_var->always_true()) { - op.block0->last().next = std::move(op.next); - ops = std::move(op.block0); - return prune_unreachable(ops); - } else if (c_var && c_var->always_false()) { - op.block1->last().next = std::move(op.next); - ops = std::move(op.block1); - return prune_unreachable(ops); - } else { - reach = prune_unreachable(op.block0) | prune_unreachable(op.block1); - } - break; - } - case Op::_While: { - // while (block0 || left) block1; - if (!prune_unreachable(op.block0)) { - // computation of block0 never returns - ops = std::move(op.block0); - return prune_unreachable(ops); - } - VarDescr* c_var = op.block0->last().var_info[op.left[0]]; - if (c_var && c_var->always_false()) { - // block1 never executed - op.block0->last().next = std::move(op.next); - ops = std::move(op.block0); - return false; - } else if (c_var && c_var->always_true()) { - if (!prune_unreachable(op.block1)) { - // block1 never returns - op.block0->last().next = std::move(op.block1); - ops = std::move(op.block0); - return false; - } - // infinite loop - op.cl = Op::_Again; - op.block0->last().next = std::move(op.block1); - op.left.clear(); - reach = false; - } else { - if (!prune_unreachable(op.block1)) { - // block1 never returns, while equivalent to block0 ; if left then block1 else next - op.cl = Op::_If; - std::unique_ptr new_op = std::move(op.block0); - op.block0 = std::move(op.block1); - op.block1 = std::make_unique(op.next->where, Op::_Nop); - new_op->last().next = std::move(ops); - ops = std::move(new_op); - } - reach = true; // block1 may be never executed - } - break; - } - case Op::_Repeat: { - // repeat (left) block0 - VarDescr* c_var = op.var_info[op.left[0]]; - if (c_var && c_var->always_nonpos()) { - // loop never executed - ops = std::move(op.next); - return prune_unreachable(ops); - } - if (c_var && c_var->always_pos()) { - if (!prune_unreachable(op.block0)) { - // block0 executed at least once, and it never returns - // replace code with block0 - ops = std::move(op.block0); - return false; - } - } else { - prune_unreachable(op.block0); - } - reach = true; - break; - } - case Op::_Until: - case Op::_Again: { - // do block0 until left; ... - if (!prune_unreachable(op.block0)) { - // block0 never returns, replace loop by block0 - ops = std::move(op.block0); - return false; - } - reach = true; - break; - } - default: - std::cerr << "fatal: unknown operation \n"; - throw src::ParseError{op.where, "unknown operation in prune_unreachable()"}; - } - if (reach) { - return prune_unreachable(op.next); - } else { - while (op.next->next) { - op.next = std::move(op.next->next); - } - return false; - } -} - -void CodeBlob::prune_unreachable_code() { - if (prune_unreachable(ops)) { - throw src::ParseError{loc, "control reaches end of function"}; - } -} - -void CodeBlob::simplify_var_types() { - for (TmpVar& var : vars) { - TypeExpr::remove_indirect(var.v_type); - } -} - -void CodeBlob::fwd_analyze() { - VarDescrList values; - assert(ops && ops->cl == Op::_Import); - for (var_idx_t i : ops->left) { - values += i; - if (vars[i].v_type->is_int()) { - values[i]->val |= VarDescr::_Int; - } - } - ops->fwd_analyze(values); -} - -void Op::prepare_args(VarDescrList values) { - if (args.size() != right.size()) { - args.clear(); - for (var_idx_t i : right) { - args.emplace_back(i); - } - } - for (std::size_t i = 0; i < right.size(); i++) { - const VarDescr* val = values[right[i]]; - if (val) { - args[i].set_value(*val); - args[i].clear_unused(); - } - } -} - -VarDescrList Op::fwd_analyze(VarDescrList values) { - var_info.import_values(values); - switch (cl) { - case _Nop: - case _Import: - break; - case _Return: - values.list.clear(); - break; - case _IntConst: { - values.add_newval(left[0]).set_const(int_const); - break; - } - case _GlobVar: { - for (var_idx_t i : left) { - values.add_newval(i); - } - break; - } - case _Call: { - prepare_args(values); - auto func = dynamic_cast(fun_ref->value); - if (func) { - std::vector res; - res.reserve(left.size()); - for (var_idx_t i : left) { - res.emplace_back(i); - } - func->compile(res, args); // abstract interpretation of res := f (args) - int j = 0; - for (var_idx_t i : left) { - values.add_newval(i).set_value(res[j++]); - } - } else { - for (var_idx_t i : left) { - values.add_newval(i); - } - } - break; - } - case _CallInd: { - for (var_idx_t i : left) { - values.add_newval(i); - } - break; - } - case _Let: { - std::vector old_val; - assert(left.size() == right.size()); - for (std::size_t i = 0; i < right.size(); i++) { - const VarDescr* ov = values[right[i]]; - assert(ov); - old_val.push_back(*ov); - } - for (std::size_t i = 0; i < left.size(); i++) { - values.add_newval(left[i]).set_value(std::move(old_val[i])); - } - break; - } - case _If: { - VarDescrList val1 = block0->fwd_analyze(values); - VarDescrList val2 = block1->fwd_analyze(std::move(values)); - values = val1 | val2; - break; - } - case _Repeat: { - bool atl1 = (values[left[0]] && values[left[0]]->always_pos()); - VarDescrList next_values = block0->fwd_analyze(values); - while (true) { - VarDescrList new_values = values | next_values; - if (same_values(new_values, values)) { - break; - } - values = std::move(new_values); - next_values = block0->fwd_analyze(values); - } - if (atl1) { - values = std::move(next_values); - } - break; - } - case _While: { - values = block0->fwd_analyze(values); - if (values[left[0]] && values[left[0]]->always_false()) { - // block1 never executed - block1->fwd_analyze(values); - break; - } - while (true) { - VarDescrList next_values = values | block0->fwd_analyze(block1->fwd_analyze(values)); - if (same_values(next_values, values)) { - break; - } - values = std::move(next_values); - } - break; - } - case _Until: - case _Again: { - while (true) { - VarDescrList next_values = values | block0->fwd_analyze(values); - if (same_values(next_values, values)) { - break; - } - values = std::move(next_values); - } - values = block0->fwd_analyze(values); - break; - } - default: - std::cerr << "fatal: unknown operation \n"; - throw src::ParseError{where, "unknown operation in fwd_analyze()"}; - } - if (next) { - return next->fwd_analyze(std::move(values)); - } else { - return values; - } -} - -bool Op::set_noreturn(bool nr) { - if (nr) { - flags |= _NoReturn; - } else { - flags &= ~_NoReturn; - } - return nr; -} - -bool Op::mark_noreturn() { - switch (cl) { - case _Nop: - if (!next) { - return set_noreturn(false); - } - // fallthrough - case _Import: - case _IntConst: - case _Let: - case _GlobVar: - case _CallInd: - case _Call: - return set_noreturn(next->mark_noreturn()); - case _Return: - return set_noreturn(true); - case _If: - return set_noreturn((block0->mark_noreturn() & block1->mark_noreturn()) | next->mark_noreturn()); - case _Again: - block0->mark_noreturn(); - return set_noreturn(false); - case _Until: - return set_noreturn(block0->mark_noreturn() | next->mark_noreturn()); - case _While: - block1->mark_noreturn(); - return set_noreturn(block0->mark_noreturn() | next->mark_noreturn()); - case _Repeat: - block0->mark_noreturn(); - return set_noreturn(next->mark_noreturn()); - default: - std::cerr << "fatal: unknown operation \n"; - throw src::ParseError{where, "unknown operation in mark_noreturn()"}; - } -} - -void CodeBlob::mark_noreturn() { - ops->mark_noreturn(); -} - -/* - * - * GENERATE CODE - * - */ - -int Stack::find(var_idx_t var, int from) const { - for (int i = from; i < depth(); i++) { - if (at(i) == var) { - return i; - } - } - return -1; -} - -int Stack::find(var_idx_t var, int from, int to) const { - for (int i = from; i < depth() && i < to; i++) { - if (at(i) == var) { - return i; - } - } - return -1; -} - -void Stack::issue_pop(int i) { - validate(i); - o << AsmOp::Pop(i); - at(i) = get(0); - s.pop_back(); - modified(); -} - -void Stack::issue_push(int i) { - validate(i); - o << AsmOp::Push(i); - s.push_back(get(i)); - modified(); -} - -void Stack::issue_xchg(int i, int j) { - validate(i); - validate(j); - if (i != j && get(i) != get(j)) { - o << AsmOp::Xchg(i, j); - std::swap(at(i), at(j)); - modified(); - } -} - -int Stack::drop_vars_except(const VarDescrList& var_info, int excl_var) { - int dropped = 0, changes; - do { - changes = 0; - int n = depth(); - for (int i = 0; i < n; i++) { - var_idx_t idx = at(i); - if ((!var_info[idx] && idx != excl_var) || find(idx, 0, i - 1) >= 0) { - // unneeded - issue_pop(i); - changes = 1; - break; - } - } - dropped += changes; - } while (changes); - return dropped; -} - -void Stack::show(int flags) { - std::ostringstream os; - for (var_idx_t i : s) { - os << ' '; - o.show_var(os, i); - } - o << AsmOp::Comment(os.str()); - mode |= _Shown; -} - -void Stack::forget_var(var_idx_t idx) { - for (auto& x : s) { - if (x == idx) { - x = _Garbage; - modified(); - } - } -} - -void Stack::push_new_var(var_idx_t idx) { - forget_var(idx); - s.push_back(idx); - modified(); -} - -void Stack::assign_var(var_idx_t new_idx, var_idx_t old_idx) { - int i = find(old_idx); - assert(i >= 0 && "variable not found in stack"); - if (new_idx != old_idx) { - at(i) = new_idx; - modified(); - } -} - -void Stack::do_copy_var(var_idx_t new_idx, var_idx_t old_idx) { - int i = find(old_idx); - assert(i >= 0 && "variable not found in stack"); - if (find(old_idx, i + 1) < 0) { - issue_push(i); - assert(at(0) == old_idx); - } - assign_var(new_idx, old_idx); -} - -void Stack::enforce_state(const StackLayout& req_stack) { - int k = (int)req_stack.size(); - for (int i = 0; i < k; i++) { - var_idx_t x = req_stack[i]; - if (i < depth() && s[i] == x) { - continue; - } - int j = find(x); - if (j >= depth() - i) { - issue_push(j); - j = 0; - } - issue_xchg(j, depth() - i - 1); - assert(s[i] == x); - } - while (depth() > k) { - issue_pop(0); - } - assert(depth() == k); - for (int i = 0; i < k; i++) { - assert(s[i] == req_stack[i]); - } -} - -void Stack::rearrange_top(const StackLayout& top, std::vector last) { - while (last.size() < top.size()) { - last.push_back(false); - } - int k = (int)top.size(); - for (int i = 0; i < k; i++) { - for (int j = i + 1; j < k; j++) { - if (top[i] == top[j]) { - last[i] = false; - break; - } - } - } - int ss = 0; - for (int i = 0; i < k; i++) { - if (last[i]) { - ++ss; - } - } - for (int i = 0; i < k; i++) { - var_idx_t x = top[i]; - if (last[i]) { - // rearrange x to be at ss(ss-1) - int j = find(x); - issue_xchg(--ss, j); - assert(get(ss) == x); - } else { - // create a new copy of x - int j = find(x); - issue_push(j); - issue_xchg(0, ss); - assert(get(ss) == x); - } - } - assert(!ss); -} - -void Stack::rearrange_top(var_idx_t top, bool last) { - int i = find(top); - if (last) { - issue_xchg(0, i); - } else { - issue_push(i); - } - assert(get(0) == top); -} - -bool Op::generate_code_step(Stack& stack) { - stack.opt_show(); - stack.drop_vars_except(var_info); - stack.opt_show(); - switch (cl) { - case _Nop: - case _Import: - return true; - case _Return: { - stack.enforce_state(left); - stack.opt_show(); - return false; - } - case _IntConst: { - stack.o << push_const(int_const); - stack.push_new_var(left[0]); - return true; - } - case _Let: { - assert(left.size() == right.size()); - int i = 0; - for (auto it = right.cbegin(); it != right.cend(); ++it) { - var_idx_t x = *it; - bool is_last = false; - if (std::find(it + 1, right.cend(), x) == right.cend()) { - auto info = var_info[x]; - if (info && info->is_last()) { - is_last = true; - } - } - if (is_last) { - stack.assign_var(--i, x); - } else { - stack.do_copy_var(--i, x); - } - } - i = 0; - for (var_idx_t x : left) { - stack.assign_var(x, --i); - } - return true; - } - case _Call: - case _CallInd: { - std::vector right1; - if (args.size()) { - for (const VarDescr& arg : args) { - if (!arg.is_unused()) { - right1.push_back(arg.idx); - } - } - } else { - right1 = right; - } - std::vector last; - for (var_idx_t x : right1) { - last.push_back(var_info[x] && var_info[x]->is_last()); - } - stack.rearrange_top(right1, std::move(last)); - stack.opt_show(); - int k = (int)stack.depth() - (int)right1.size(); - assert(k >= 0); - for (int i = 0; i < (int)right1.size(); i++) { - assert(stack.s[k + i] == right1[i]); - } - if (cl == _CallInd) { - stack.o << exec_arg_op("CALLARGS", (int)right.size() - 1, -1, (int)right.size() - 1); - } else { - auto func = dynamic_cast(fun_ref->value); - if (func) { - std::vector res; - res.reserve(left.size()); - for (var_idx_t i : left) { - res.emplace_back(i); - } - stack.o << func->compile(res, args); // compile res := f (args) - } else { - std::string name = symbols.get_name(fun_ref->sym_idx); - stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size()); - } - } - stack.s.resize(k); - for (int i = 0; i < (int)left.size(); i++) { - stack.push_new_var(left[i]); - } - return true; - } - case _If: { - if (block0->is_empty() && block1->is_empty()) { - return true; - } - var_idx_t x = left[0]; - stack.rearrange_top(x, var_info[x] && var_info[x]->is_last()); - assert(stack.get(0) == x); - stack.opt_show(); - stack.s.pop_back(); - stack.modified(); - if (block1->is_empty()) { - // if (left) block0; ... - if (block0->noreturn()) { - stack.o << "IFJMP:<{"; - stack.o.indent(); - Stack stack_copy{stack}; - block0->generate_code_all(stack_copy); - stack.o.undent(); - stack.o << "}>"; - return true; - } - stack.o << "IF:<{"; - stack.o.indent(); - Stack stack_copy{stack}; - block0->generate_code_all(stack_copy); - stack_copy.drop_vars_except(var_info); - stack_copy.opt_show(); - if (stack_copy == stack) { - stack.o.undent(); - stack.o << "}>"; - return true; - } - stack_copy.drop_vars_except(next->var_info); - stack_copy.opt_show(); - if (stack_copy == stack) { - stack.o.undent(); - stack.o << "}>"; - return true; - } - stack.o.undent(); - stack.o << "}>ELSE<{"; - stack.o.indent(); - stack.enforce_state(stack_copy.s); - stack.opt_show(); - stack.o.undent(); - stack.o << "}>"; - return true; - } - if (block0->is_empty()) { - // if (!left) block1; ... - if (block1->noreturn()) { - stack.o << "IFNOTJMP:<{"; - stack.o.indent(); - Stack stack_copy{stack}; - block1->generate_code_all(stack_copy); - stack.o.undent(); - stack.o << "}>"; - return true; - } - stack.o << "IFNOT:<{"; - stack.o.indent(); - Stack stack_copy{stack}; - block1->generate_code_all(stack_copy); - stack_copy.drop_vars_except(var_info); - stack_copy.opt_show(); - if (stack_copy == stack) { - stack.o.undent(); - stack.o << "}>"; - return true; - } - stack_copy.drop_vars_except(next->var_info); - stack_copy.opt_show(); - if (stack_copy == stack) { - stack.o.undent(); - stack.o << "}>"; - return true; - } - stack.o.undent(); - stack.o << "}>ELSE<{"; - stack.o.indent(); - stack.enforce_state(stack_copy.s); - stack.opt_show(); - stack.o.undent(); - stack.o << "}>"; - return true; - } - if (block0->noreturn()) { - stack.o << "IFJMP:<{"; - stack.o.indent(); - Stack stack_copy{stack}; - block0->generate_code_all(stack_copy); - stack.o.undent(); - stack.o << "}>"; - block1->generate_code_all(stack); - return true; - } - if (block1->noreturn()) { - stack.o << "IFNOTJMP:<{"; - stack.o.indent(); - Stack stack_copy{stack}; - block1->generate_code_all(stack_copy); - stack.o.undent(); - stack.o << "}>"; - block0->generate_code_all(stack); - return true; - } - stack.o << "IF:<{"; - stack.o.indent(); - Stack stack_copy{stack}; - block0->generate_code_all(stack_copy); - stack_copy.drop_vars_except(next->var_info); - stack_copy.opt_show(); - stack.o.undent(); - stack.o << "}>ELSE<{"; - stack.o.indent(); - block1->generate_code_all(stack); - stack.enforce_state(stack_copy.s); - stack.opt_show(); - stack.o.undent(); - stack.o << "}>"; - return true; - } - case _Repeat: { - var_idx_t x = left[0]; - //stack.drop_vars_except(block0->var_info, x); - stack.rearrange_top(x, var_info[x] && var_info[x]->is_last()); - assert(stack.get(0) == x); - stack.opt_show(); - stack.s.pop_back(); - stack.modified(); - if (!next->is_empty()) { - stack.o << "REPEAT:<{"; - stack.o.indent(); - StackLayout layout1 = stack.s; - block0->generate_code_all(stack); - stack.enforce_state(std::move(layout1)); - stack.opt_show(); - stack.o.undent(); - stack.o << "}>"; - return true; - } else { - stack.o << "REPEATEND"; - StackLayout layout1 = stack.s; - block0->generate_code_all(stack); - stack.enforce_state(std::move(layout1)); - stack.opt_show(); - return false; - } - } - case _Again: { - stack.drop_vars_except(block0->var_info); - stack.opt_show(); - if (!next->is_empty()) { - stack.o << "AGAIN:<{"; - stack.o.indent(); - StackLayout layout1 = stack.s; - block0->generate_code_all(stack); - stack.enforce_state(std::move(layout1)); - stack.opt_show(); - stack.o.undent(); - stack.o << "}>"; - return true; - } else { - stack.o << "AGAINEND"; - StackLayout layout1 = stack.s; - block0->generate_code_all(stack); - stack.enforce_state(std::move(layout1)); - stack.opt_show(); - return false; - } - } - case _Until: { - // stack.drop_vars_except(block0->var_info); - // stack.opt_show(); - if (!next->is_empty()) { - stack.o << "UNTIL:<{"; - stack.o.indent(); - StackLayout layout1 = stack.s; - block0->generate_code_all(stack); - layout1.push_back(left[0]); - stack.enforce_state(std::move(layout1)); - stack.opt_show(); - stack.o.undent(); - stack.o << "}>"; - stack.s.pop_back(); - stack.modified(); - return true; - } else { - stack.o << "UNTILEND"; - StackLayout layout1 = stack.s; - block0->generate_code_all(stack); - layout1.push_back(left[0]); - stack.enforce_state(std::move(layout1)); - stack.opt_show(); - return false; - } - } - case _While: { - // while (block0 | left) block1; ...next - var_idx_t x = left[0]; - stack.drop_vars_except(block0->var_info); - stack.opt_show(); - StackLayout layout1 = stack.s; - bool next_empty = next->is_empty(); - stack.o << (next_empty ? "WHILEEND:<{" : "WHILE:<{"); - stack.o.indent(); - block0->generate_code_all(stack); - stack.rearrange_top(x, !next->var_info[x] && !block1->var_info[x]); - stack.opt_show(); - stack.s.pop_back(); - stack.modified(); - stack.o.undent(); - Stack stack_copy{stack}; - stack.o << (next_empty ? "}>" : "}>DO<{"); - if (!next_empty) { - stack.o.indent(); - } - stack_copy.opt_show(); - block1->generate_code_all(stack_copy); - stack_copy.enforce_state(std::move(layout1)); - stack_copy.opt_show(); - if (!next_empty) { - stack.o.undent(); - stack.o << "}>"; - return true; - } else { - return false; - } - } - default: - std::cerr << "fatal: unknown operation \n"; - throw src::ParseError{where, "unknown operation in generate_code()"}; - } -} - -bool Op::generate_code_all(Stack& stack) { - if (generate_code_step(stack) && next) { - return next->generate_code_all(stack); - } else { - return false; - } -} - -void CodeBlob::generate_code(AsmOpList& out, int mode) { - Stack stack{out, mode}; - assert(ops && ops->cl == Op::_Import); - for (var_idx_t x : ops->left) { - stack.push_new_var(x); - } - ops->generate_code_all(stack); -} - -void CodeBlob::generate_code(std::ostream& os, int mode, int indent) { - AsmOpList out_list(indent, &vars); - generate_code(out_list, mode); - out_list.out(os, mode); -} - void generate_output_func(SymDef* func_sym) { - SymValFunc* func_val = dynamic_cast(func_sym->value); + SymValCodeFunc* func_val = dynamic_cast(func_sym->value); assert(func_val); - std::string name = symbols.get_name(func_sym->sym_idx); + std::string name = sym::symbols.get_name(func_sym->sym_idx); std::cerr << "\n\n=========================\nfunction " << name << " : " << func_val->get_type() << std::endl; if (!func_val->code) { std::cerr << "( undefined )\n"; } else { CodeBlob& code = *(func_val->code); - code.print(std::cerr, 1); + code.print(std::cerr, 9); code.simplify_var_types(); + // std::cerr << "after simplify_var_types: \n"; code.print(std::cerr, 0); code.prune_unreachable_code(); + // std::cerr << "after prune_unreachable: \n"; code.print(std::cerr, 0); code.split_vars(true); + // std::cerr << "after split_vars: \n"; code.print(std::cerr, 0); for (int i = 0; i < 8; i++) { code.compute_used_code_vars(); + // std::cerr << "after compute_used_vars: \n"; code.print(std::cerr, 6); code.fwd_analyze(); + // std::cerr << "after fwd_analyze: \n"; code.print(std::cerr, 6); code.prune_unreachable_code(); + // std::cerr << "after prune_unreachable: \n"; code.print(std::cerr, 6); } code.mark_noreturn(); - code.print(std::cerr, 7); + code.print(std::cerr, 15); std::cerr << "\n---------- resulting code for " << name << " -------------\n"; code.generate_code(std::cout, Stack::_StkCmt | Stack::_CptStkCmt); std::cerr << "--------------\n"; @@ -3937,7 +50,7 @@ int generate_output() { try { generate_output_func(func_sym); } catch (src::Error& err) { - std::cerr << "cannot generate code for function `" << symbols.get_name(func_sym->sym_idx) << "`:\n" + std::cerr << "cannot generate code for function `" << sym::symbols.get_name(func_sym->sym_idx) << "`:\n" << err << std::endl; ++errors; } @@ -3971,7 +84,7 @@ int main(int argc, char* const argv[]) { } } - src::define_keywords(); + funC::define_keywords(); funC::define_builtins(); int ok = 0, proc = 0; diff --git a/ton-test-liteclient-full/lite-client/crypto/func/func.h b/ton-test-liteclient-full/lite-client/crypto/func/func.h index 58ed27c..8e144df 100644 --- a/ton-test-liteclient-full/lite-client/crypto/func/func.h +++ b/ton-test-liteclient-full/lite-client/crypto/func/func.h @@ -1,16 +1,17 @@ #pragma once #include -#include #include #include #include #include #include -#include #include #include "common/refcnt.hpp" #include "common/bigint.hpp" #include "common/refint.h" +#include "parser/srcread.h" +#include "parser/lexer.h" +#include "parser/symtable.h" namespace funC { @@ -18,6 +19,8 @@ enum Keyword { _Eof = -1, _Ident = 0, _Number, + _Special, + _String, _Return = 0x80, _Var, _Repeat, @@ -32,6 +35,7 @@ enum Keyword { _Neq, _Leq, _Geq, + _Spaceship, _Lshift, _Rshift, _RshiftR, @@ -57,6 +61,8 @@ enum Keyword { _Cont, _Type, _Mapsto, + _Asm, + _Impure, _Extern, _Operator, _Infix, @@ -64,9 +70,24 @@ enum Keyword { _Infixr }; -} // namespace funC +void define_keywords(); -namespace funC { +class IdSc { + int cls; + + public: + enum { undef = 0, dotid = 1, tildeid = 2 }; + IdSc(int _cls = undef) : cls(_cls) { + } + operator int() { + return cls; + } +}; + +// symbol subclass: +// 1 = begins with . (a const method) +// 2 = begins with ~ (a non-const method) +// 0 = else /* * @@ -109,6 +130,12 @@ struct TypeExpr { bool is_int() const { return is_atomic(_Int); } + bool has_fixed_width() const { + return minw == maxw; + } + int get_width() const { + return has_fixed_width() ? minw : -1; + } void compute_width(); bool recompute_width(); void show_width(std::ostream& os); @@ -138,6 +165,22 @@ struct TypeExpr { static bool remove_indirect(TypeExpr*& te, TypeExpr* forbidden = nullptr); }; +std::ostream& operator<<(std::ostream& os, TypeExpr* type_expr); + +struct UniformizeError { + TypeExpr* te1; + TypeExpr* te2; + std::string msg; + UniformizeError(TypeExpr* _te1, TypeExpr* _te2, std::string _msg = "") : te1(_te1), te2(_te2), msg(_msg) { + } + void print_message(std::ostream& os) const; + std::string message() const; +}; + +std::ostream& operator<<(std::ostream& os, const UniformizeError& ue); + +void uniformize(TypeExpr*& te1, TypeExpr*& te2); + // extern int TypeExpr::holes; /* @@ -148,9 +191,10 @@ struct TypeExpr { using src::Lexem; using src::SrcLocation; -using src::sym_idx_t; using sym::SymDef; +using sym::sym_idx_t; using sym::var_idx_t; +using const_idx_t = int; struct TmpVar { TypeExpr* v_type; @@ -187,10 +231,17 @@ struct VarDescr { static constexpr int ConstZero = _Int | _Zero | _Pos | _Neg | _Bool | _Bit | _Finite | _Even; static constexpr int ConstOne = _Int | _NonZero | _Pos | _Bit | _Finite | _Odd; static constexpr int ConstTrue = _Int | _NonZero | _Neg | _Bool | _Finite | _Odd; + static constexpr int ValBit = ConstZero & ConstOne; + static constexpr int ValBool = ConstZero & ConstTrue; + static constexpr int FiniteInt = _Int | _Finite; + static constexpr int FiniteUInt = FiniteInt | _Pos; int val; td::RefInt256 int_const; VarDescr(var_idx_t _idx = -1, int _flags = 0, int _val = 0) : idx(_idx), flags(_flags), val(_val) { } + bool operator<(var_idx_t other_idx) const { + return idx < other_idx; + } bool is_unused() const { return flags & _Unused; } @@ -209,6 +260,12 @@ struct VarDescr { bool always_zero() const { return val & _Zero; } + bool always_even() const { + return val & _Even; + } + bool always_odd() const { + return val & _Odd; + } bool is_const() const { return val & _Const; } @@ -230,22 +287,38 @@ struct VarDescr { bool always_finite() const { return val & _Finite; } + bool always_less(const VarDescr& other) const; + bool always_leq(const VarDescr& other) const; + bool always_greater(const VarDescr& other) const; + bool always_geq(const VarDescr& other) const; + bool always_equal(const VarDescr& other) const; + bool always_neq(const VarDescr& other) const; void unused() { flags |= _Unused; } void clear_unused() { flags &= ~_Unused; } + void set_const(long long value); void set_const(td::RefInt256 value); void set_const_nan(); + void operator+=(const VarDescr& y) { + flags &= y.flags; + } void operator|=(const VarDescr& y); void operator&=(const VarDescr& y); void set_value(const VarDescr& y); void set_value(VarDescr&& y); void clear_value(); void show_value(std::ostream& os) const; + void show(std::ostream& os, const char* var_name = nullptr) const; }; +inline std::ostream& operator<<(std::ostream& os, const VarDescr& vd) { + vd.show(os); + return os; +} + struct VarDescrList { std::vector list; VarDescrList() : list() { @@ -262,19 +335,32 @@ struct VarDescrList { VarDescrList operator+(const VarDescrList& y) const; VarDescrList& operator+=(const VarDescrList& y); VarDescrList& clear_last(); - VarDescrList& operator+=(const std::vector& idx_list); - VarDescrList& operator+=(var_idx_t idx); + VarDescrList& operator+=(var_idx_t idx) { + return add_var(idx); + } + VarDescrList& operator+=(const std::vector& idx_list) { + return add_vars(idx_list); + } + VarDescrList& add_var(var_idx_t idx, bool unused = false); + VarDescrList& add_vars(const std::vector& idx_list, bool unused = false); VarDescrList& operator-=(const std::vector& idx_list); VarDescrList& operator-=(var_idx_t idx); std::size_t count(const std::vector idx_list) const; + std::size_t count_used(const std::vector idx_list) const; VarDescr& add(var_idx_t idx); VarDescr& add_newval(var_idx_t idx); VarDescrList& operator&=(const VarDescrList& values); VarDescrList& import_values(const VarDescrList& values); VarDescrList operator|(const VarDescrList& y) const; VarDescrList& operator|=(const VarDescrList& values); + void show(std::ostream& os) const; }; +inline std::ostream& operator<<(std::ostream& os, const VarDescrList& values) { + values.show(os); + return os; +} + struct CodeBlob; template @@ -329,7 +415,7 @@ struct Op { _Again }; int cl; - enum { _Disabled = 1, _Reachable = 2, _NoReturn = 4 }; + enum { _Disabled = 1, _Reachable = 2, _NoReturn = 4, _ImpureR = 8, _ImpureW = 16, _Impure = 24 }; int flags; std::unique_ptr next; SymDef* fun_ref; @@ -379,9 +465,11 @@ struct Op { void split_vars(const std::vector& vars); static void split_var_list(std::vector& var_list, const std::vector& vars); bool compute_used_vars(const CodeBlob& code, bool edit); - bool std_compute_used_vars(); + bool std_compute_used_vars(bool disabled = false); bool set_var_info(const VarDescrList& new_var_info); bool set_var_info(VarDescrList&& new_var_info); + bool set_var_info_except(const VarDescrList& new_var_info, const std::vector& var_list); + bool set_var_info_except(VarDescrList&& new_var_info, const std::vector& var_list); void prepare_args(VarDescrList values); VarDescrList fwd_analyze(VarDescrList values); bool set_noreturn(bool nr); @@ -392,6 +480,9 @@ struct Op { bool is_empty() const { return cl == _Nop && !next; } + bool is_pure() const { + return !(flags & _Impure); + } bool generate_code_step(Stack& stack); bool generate_code_all(Stack& stack); Op& last() { @@ -414,6 +505,38 @@ struct Op { } }; +inline ListIterator begin(const std::unique_ptr& op_list) { + return ListIterator{op_list.get()}; +} + +inline ListIterator end(const std::unique_ptr& op_list) { + return ListIterator{}; +} + +inline ListIterator cbegin(const Op* op_list) { + return ListIterator{op_list}; +} + +inline ListIterator cend(const Op* op_list) { + return ListIterator{}; +} + +inline ListIterator begin(const Op* op_list) { + return ListIterator{op_list}; +} + +inline ListIterator end(const Op* op_list) { + return ListIterator{}; +} + +inline ListIterator begin(Op* op_list) { + return ListIterator{op_list}; +} + +inline ListIterator end(Op* op_list) { + return ListIterator{}; +} + typedef std::tuple FormalArg; typedef std::vector FormalArgList; @@ -474,23 +597,61 @@ struct CodeBlob { struct SymVal : sym::SymValBase { TypeExpr* sym_type; - SymVal(int _type, int _idx, TypeExpr* _stype = nullptr) : sym::SymValBase(_type, _idx), sym_type(_stype) { + bool impure; + SymVal(int _type, int _idx, TypeExpr* _stype = nullptr, bool _impure = false) + : sym::SymValBase(_type, _idx), sym_type(_stype), impure(_impure) { } + ~SymVal() override = default; TypeExpr* get_type() const { return sym_type; } + virtual const std::vector* get_arg_order() const { + return nullptr; + } + virtual const std::vector* get_ret_order() const { + return nullptr; + } }; struct SymValFunc : SymVal { - CodeBlob* code; + std::vector arg_order, ret_order; ~SymValFunc() override = default; - SymValFunc(int val, TypeExpr* _ft) : SymVal(_Func, val, _ft), code(nullptr) { + SymValFunc(int val, TypeExpr* _ft, bool _impure = false) : SymVal(_Func, val, _ft, _impure) { + } + SymValFunc(int val, TypeExpr* _ft, std::initializer_list _arg_order, std::initializer_list _ret_order = {}, + bool _impure = false) + : SymVal(_Func, val, _ft, _impure), arg_order(_arg_order), ret_order(_ret_order) { + } + + const std::vector* get_arg_order() const override { + return arg_order.empty() ? nullptr : &arg_order; + } + const std::vector* get_ret_order() const override { + return ret_order.empty() ? nullptr : &ret_order; + } +}; + +struct SymValCodeFunc : SymValFunc { + CodeBlob* code; + ~SymValCodeFunc() override = default; + SymValCodeFunc(int val, TypeExpr* _ft, bool _impure = false) : SymValFunc(val, _ft, _impure), code(nullptr) { } }; extern int glob_func_cnt, undef_func_cnt; extern std::vector glob_func; +/* + * + * PARSE SOURCE + * + */ + +// defined in parse-func.cpp +bool parse_source(std::istream* is, const src::FileDescr* fdescr); +bool parse_source_file(const char* filename); +bool parse_source_stdin(); + /* * * EXPRESSIONS @@ -498,25 +659,41 @@ extern std::vector glob_func; */ struct Expr { - enum { _None, _Apply, _VarApply, _TypeApply, _Tuple, _Const, _Var, _Glob, _Letop, _Hole, _Type }; + enum { + _None, + _Apply, + _VarApply, + _TypeApply, + _Tuple, + _Const, + _Var, + _Glob, + _Letop, + _LetFirst, + _Hole, + _Type, + _CondExpr + }; int cls; - int val; - enum { _IsType = 1, _IsRvalue = 2, _IsLvalue = 4, _IsHole = 8, _IsNewVar = 16 }; - int flags; + int val{0}; + enum { _IsType = 1, _IsRvalue = 2, _IsLvalue = 4, _IsHole = 8, _IsNewVar = 16, _IsImpure = 32 }; + int flags{0}; SrcLocation here; td::RefInt256 intval; - SymDef* sym; - TypeExpr* e_type; + SymDef* sym{nullptr}; + TypeExpr* e_type{nullptr}; std::vector args; - Expr(int c = _None) : cls(c), val(0), flags(0), here(), sym(nullptr), e_type(nullptr) { + Expr(int c = _None) : cls(c) { + } + Expr(int c, const SrcLocation& loc) : cls(c), here(loc) { } - Expr(int c, const SrcLocation& loc) : cls(c), val(0), flags(0), here(loc), sym(nullptr), e_type(nullptr) { + Expr(int c, std::vector _args) : cls(c), args(std::move(_args)) { } - Expr(int c, std::vector _args) - : cls(c), val(0), flags(0), here(), sym(nullptr), e_type(nullptr), args(std::move(_args)) { + Expr(int c, std::initializer_list _arglist) : cls(c), args(std::move(_arglist)) { } - Expr(int c, std::initializer_list _arglist) - : cls(c), val(0), flags(0), here(), sym(nullptr), e_type(nullptr), args(std::move(_arglist)) { + Expr(int c, SymDef* _sym, std::initializer_list _arglist) : cls(c), sym(_sym), args(std::move(_arglist)) { + } + Expr(int c, SymDef* _sym, std::vector _arglist) : cls(c), sym(_sym), args(std::move(_arglist)) { } Expr(int c, sym_idx_t name_idx, std::initializer_list _arglist); ~Expr() { @@ -562,6 +739,10 @@ struct Expr { */ typedef std::vector StackLayout; +typedef std::pair var_const_idx_t; +typedef std::vector StackLayoutExt; +constexpr const_idx_t not_const = -1; +using Const = td::RefInt256; struct AsmOp { enum Type { a_none, a_xchg, a_push, a_pop, a_const, a_custom, a_magic }; @@ -595,18 +776,93 @@ struct AsmOp { bool is_comment() const { return t == a_none && !op.empty(); } + bool is_custom() const { + return t == a_custom; + } + bool is_very_custom() const { + return is_custom() && a >= 255; + } + bool is_push() const { + return t == a_push; + } + bool is_push(int x) const { + return is_push() && a == x; + } + bool is_pop() const { + return t == a_pop; + } + bool is_pop(int x) const { + return is_pop() && a == x; + } + bool is_xchg() const { + return t == a_xchg; + } + bool is_xchg(int x, int y) const { + return t == a_xchg && b == y && a == x; + } + bool is_swap() const { + return is_xchg(0, 1); + } + bool is_const() const { + return t == a_const && !a && b == 1; + } + bool is_gconst() const { + return (t == a_const || t == a_custom) && !a && b == 1; + } static AsmOp Nop() { return AsmOp(a_none); } - static AsmOp Xchg(int a, int b) { + static AsmOp Xchg(int a, int b = 0) { return a == b ? AsmOp(a_none) : (a < b ? AsmOp(a_xchg, a, b) : AsmOp(a_xchg, b, a)); } static AsmOp Push(int a) { return AsmOp(a_push, a); } - static AsmOp Pop(int a) { + static AsmOp Pop(int a = 0) { return AsmOp(a_pop, a); } + static AsmOp Xchg2(int a, int b) { + return make_stk2(a, b, "XCHG2", 0); + } + static AsmOp XcPu(int a, int b) { + return make_stk2(a, b, "XCPU", 1); + } + static AsmOp PuXc(int a, int b) { + return make_stk2(a, b, "PUXC", 1); + } + static AsmOp Push2(int a, int b) { + return make_stk2(a, b, "PUSH2", 2); + } + static AsmOp Xchg3(int a, int b, int c) { + return make_stk3(a, b, c, "XCHG3", 0); + } + static AsmOp Xc2Pu(int a, int b, int c) { + return make_stk3(a, b, c, "XC2PU", 1); + } + static AsmOp XcPuXc(int a, int b, int c) { + return make_stk3(a, b, c, "XCPUXC", 1); + } + static AsmOp XcPu2(int a, int b, int c) { + return make_stk3(a, b, c, "XCPU2", 3); + } + static AsmOp PuXc2(int a, int b, int c) { + return make_stk3(a, b, c, "PUXC2", 3); + } + static AsmOp PuXcPu(int a, int b, int c) { + return make_stk3(a, b, c, "PUXCPU", 3); + } + static AsmOp Pu2Xc(int a, int b, int c) { + return make_stk3(a, b, c, "PU2XC", 3); + } + static AsmOp Push3(int a, int b, int c) { + return make_stk3(a, b, c, "PUSH3", 3); + } + static AsmOp BlkSwap(int a, int b); + static AsmOp BlkPush(int a, int b); + static AsmOp BlkDrop(int a); + static AsmOp BlkReverse(int a, int b); + static AsmOp make_stk2(int a, int b, const char* str, int delta); + static AsmOp make_stk3(int a, int b, int c, const char* str, int delta); static AsmOp IntConst(td::RefInt256 value); static AsmOp Const(std::string push_op) { return AsmOp(a_const, 0, 1, std::move(push_op)); @@ -624,7 +880,7 @@ struct AsmOp { }; inline std::ostream& operator<<(std::ostream& os, const AsmOp& op) { - op.out_indent_nl(os); + op.out(os); return os; } @@ -632,6 +888,7 @@ struct AsmOpList { std::vector list_; int indent_{0}; const std::vector* var_names_{nullptr}; + std::vector constants_; void out(std::ostream& os, int mode = 0) const; AsmOpList(int indent = 0, const std::vector* var_names = nullptr) : indent_(indent), var_names_(var_names) { } @@ -650,7 +907,10 @@ struct AsmOpList { AsmOpList& operator<<(std::string str) { return add(AsmOp::Type::a_custom, 255, 255, str); } + const_idx_t register_const(Const new_const); + Const get_const(const_idx_t idx); void show_var(std::ostream& os, var_idx_t idx) const; + void show_var_ext(std::ostream& os, std::pair idx_pair) const; void adjust_last() { if (list_.back().is_nop()) { list_.pop_back(); @@ -686,42 +946,300 @@ class IndentGuard { } }; +struct AsmOpCons { + std::unique_ptr car; + std::unique_ptr cdr; + AsmOpCons(std::unique_ptr head, std::unique_ptr tail) : car(std::move(head)), cdr(std::move(tail)) { + } + static std::unique_ptr cons(std::unique_ptr head, std::unique_ptr tail) { + return std::make_unique(std::move(head), std::move(tail)); + } +}; + +using AsmOpConsList = std::unique_ptr; + +int is_pos_pow2(td::RefInt256 x); +int is_neg_pow2(td::RefInt256 x); + +/* + * + * STACK TRANSFORMS + * + */ + +/* +A stack transform is a map f:N={0,1,...} -> N, such that f(x) = x + d_f for almost all x:N and for a fixed d_f:N. +They form a monoid under composition: (fg)(x)=f(g(x)). +They act on stacks S on the right: Sf=S', such that S'[n]=S[f(n)]. + +A stack transform f is determined by d_f and the finite set A of all pairs (x,y), such that x>=d_f, f(x-d_f) = y and y<>x. They are listed in increasing order by x. +*/ +struct StackTransform { + enum { max_n = 8, inf_x = 0x7fffffff, c_start = -1000 }; + int d{0}, n{0}, dp{0}, c{0}; + bool invalid{false}; + std::array, max_n> A; + StackTransform() = default; + // list of f(0),f(1),...,f(s); assumes next values are f(s)+1,f(s)+2,... + StackTransform(std::initializer_list list); + StackTransform& operator=(std::initializer_list list); + bool assign(const StackTransform& other); + static StackTransform id() { + return {}; + } + bool invalidate() { + invalid = true; + return false; + } + bool is_valid() const { + return !invalid; + } + bool set_id() { + d = n = dp = c = 0; + invalid = false; + return true; + } + bool shift(int offs) { // post-composes with x -> x + offs + d += offs; + return offs <= 0 || remove_negative(); + } + bool remove_negative(); + bool touch(int i) { + dp = std::max(dp, i + d + 1); + return true; + } + bool is_permutation() const; // is f:N->N bijective ? + bool is_trivial_after(int x) const; // f(x') = x' + d for all x' >= x + int preimage_count(int y) const; // card f^{-1}(y) + std::vector preimage(int y) const; + bool apply_xchg(int i, int j, bool relaxed = false); + bool apply_push(int i); + bool apply_pop(int i = 0); + bool apply_push_newconst(); + bool apply(const StackTransform& other); // this = this * other + bool preapply(const StackTransform& other); // this = other * this + // c := a * b + static bool compose(const StackTransform& a, const StackTransform& b, StackTransform& c); + StackTransform& operator*=(const StackTransform& other); + StackTransform operator*(const StackTransform& b) const &; + bool equal(const StackTransform& other, bool relaxed = false) const; + bool almost_equal(const StackTransform& other) const { + return equal(other, true); + } + bool operator==(const StackTransform& other) const { + return dp == other.dp && almost_equal(other); + } + bool operator<=(const StackTransform& other) const { + return dp <= other.dp && almost_equal(other); + } + bool operator>=(const StackTransform& other) const { + return dp >= other.dp && almost_equal(other); + } + int get(int i) const; + int touch_get(int i, bool relaxed = false) { + if (!relaxed) { + touch(i); + } + return get(i); + } + bool set(int i, int v, bool relaxed = false); + int operator()(int i) const { + return get(i); + } + class Pos { + StackTransform& t_; + int p_; + + public: + Pos(StackTransform& t, int p) : t_(t), p_(p) { + } + Pos& operator=(const Pos& other) = delete; + operator int() const { + return t_.get(p_); + } + const Pos& operator=(int v) const { + t_.set(p_, v); + return *this; + } + }; + Pos operator[](int i) { + return Pos(*this, i); + } + static const StackTransform rot; + static const StackTransform rot_rev; + bool is_id() const { + return is_valid() && !d && !n; + } + bool is_xchg(int i, int j) const; + bool is_xchg(int* i, int* j) const; + bool is_push(int i) const; + bool is_push(int* i) const; + bool is_pop(int i) const; + bool is_pop(int* i) const; + bool is_rot() const; + bool is_rotrev() const; + bool is_xchg2(int i, int j) const; + bool is_xchg2(int* i, int* j) const; + bool is_xcpu(int i, int j) const; + bool is_xcpu(int* i, int* j) const; + bool is_puxc(int i, int j) const; + bool is_puxc(int* i, int* j) const; + bool is_push2(int i, int j) const; + bool is_push2(int* i, int* j) const; + bool is_xchg3(int* i, int* j, int* k) const; + bool is_xc2pu(int* i, int* j, int* k) const; + bool is_xcpuxc(int* i, int* j, int* k) const; + bool is_xcpu2(int* i, int* j, int* k) const; + bool is_puxc2(int i, int j, int k) const; + bool is_puxc2(int* i, int* j, int* k) const; + bool is_puxcpu(int* i, int* j, int* k) const; + bool is_pu2xc(int i, int j, int k) const; + bool is_pu2xc(int* i, int* j, int* k) const; + bool is_push3(int i, int j, int k) const; + bool is_push3(int* i, int* j, int* k) const; + bool is_blkswap(int i, int j) const; + bool is_blkswap(int* i, int* j) const; + bool is_blkpush(int i, int j) const; + bool is_blkpush(int* i, int* j) const; + bool is_blkdrop(int* i) const; + bool is_reverse(int i, int j) const; + bool is_reverse(int* i, int* j) const; + + void show(std::ostream& os, int mode = 0) const; + + static StackTransform Xchg(int i, int j, bool relaxed = false); + static StackTransform Push(int i); + static StackTransform Pop(int i); + + private: + int try_load(int& i, int offs = 0) const; // returns A[i++].first + offs or inf_x + bool try_store(int x, int y); // appends (x,y) to A +}; + +//extern const StackTransform StackTransform::rot, StackTransform::rot_rev; + +inline std::ostream& operator<<(std::ostream& os, const StackTransform& trans) { + trans.show(os); + return os; +} + +bool apply_op(StackTransform& trans, const AsmOp& op); + +/* + * + * STACK OPERATION OPTIMIZER + * + */ + +struct Optimizer { + enum { n = 6 }; + AsmOpConsList code_; + int l_{0}, l2_{0}, p_, pb_, q_, indent_; + bool debug_{false}; + std::unique_ptr op_[n], oq_[n]; + AsmOpCons* op_cons_[n]; + int offs_[n]; + StackTransform tr_[n]; + Optimizer() { + } + Optimizer(bool debug) : debug_(debug) { + } + Optimizer(AsmOpConsList code, bool debug = false) : Optimizer(debug) { + set_code(std::move(code)); + } + void set_code(AsmOpConsList code_); + void unpack(); + void pack(); + void apply(); + bool find_at_least(int pb); + bool find(); + bool optimize(); + bool compute_stack_transforms(); + bool say(std::string str) const; + bool show_stack_transforms() const; + void show_head() const; + void show_left() const; + void show_right() const; + bool is_const_push_swap() const; + bool rewrite_const_push_swap(); + bool simple_rewrite(int p, AsmOp&& new_op); + bool simple_rewrite(AsmOp&& new_op) { + return simple_rewrite(p_, std::move(new_op)); + } + bool simple_rewrite_nop(); + bool is_pred(const std::function& pred, int min_p = 2); + bool is_same_as(const StackTransform& trans, int min_p = 2); + bool is_rot(); + bool is_rotrev(); + bool is_tuck(); + bool is_2dup(); + bool is_2drop(); + bool is_2swap(); + bool is_2over(); + bool is_xchg(int* i, int* j); + bool is_push(int* i); + bool is_pop(int* i); + bool is_nop(); + bool is_xchg2(int* i, int* j); + bool is_xcpu(int* i, int* j); + bool is_puxc(int* i, int* j); + bool is_push2(int* i, int* j); + bool is_xchg3(int* i, int* j, int* k); + bool is_xc2pu(int* i, int* j, int* k); + bool is_xcpuxc(int* i, int* j, int* k); + bool is_xcpu2(int* i, int* j, int* k); + bool is_puxc2(int* i, int* j, int* k); + bool is_puxcpu(int* i, int* j, int* k); + bool is_pu2xc(int* i, int* j, int* k); + bool is_push3(int* i, int* j, int* k); + bool is_blkswap(int* i, int* j); + bool is_blkpush(int* i, int* j); + bool is_blkdrop(int* i); + bool is_reverse(int* i, int* j); + AsmOpConsList extract_code(); +}; + +AsmOpConsList optimize_code_head(AsmOpConsList op_list); +AsmOpConsList optimize_code(AsmOpConsList op_list); +void optimize_code(AsmOpList& ops); + struct Stack { - StackLayout s; + StackLayoutExt s; AsmOpList& o; enum { _StkCmt = 1, _CptStkCmt = 2, _Shown = 256, _Garbage = -0x10000 }; int mode; Stack(AsmOpList& _o, int _mode = 0) : o(_o), mode(_mode) { } - Stack(AsmOpList& _o, const StackLayout& _s, int _mode = 0) : s(_s), o(_o), mode(_mode) { + Stack(AsmOpList& _o, const StackLayoutExt& _s, int _mode = 0) : s(_s), o(_o), mode(_mode) { } - Stack(AsmOpList& _o, StackLayout&& _s, int _mode = 0) : s(std::move(_s)), o(_o), mode(_mode) { + Stack(AsmOpList& _o, StackLayoutExt&& _s, int _mode = 0) : s(std::move(_s)), o(_o), mode(_mode) { } int depth() const { return (int)s.size(); } - var_idx_t& operator[](int i) { - return s[depth() - i - 1]; - } var_idx_t operator[](int i) const { - return s[depth() - i - 1]; - } - var_idx_t& at(int i) { validate(i); - return operator[](i); + return s[depth() - i - 1].first; } - var_idx_t at(int i) const { + var_const_idx_t& at(int i) { validate(i); - return operator[](i); + return s[depth() - i - 1]; } - var_idx_t get(int i) const { + var_const_idx_t at(int i) const { validate(i); - return operator[](i); + return s[depth() - i - 1]; } + var_const_idx_t get(int i) const { + return at(i); + } + StackLayout vars() const; int find(var_idx_t var, int from = 0) const; int find(var_idx_t var, int from, int to) const; + int find_const(const_idx_t cst, int from = 0) const; + int find_outside(var_idx_t var, int from, int to) const; + void forget_const(); void validate(int i) const { - assert(i >= 0 && i < depth()); + assert(i >= 0 && i < depth() && "invalid stack reference"); } void modified() { mode &= ~_Shown; @@ -732,11 +1250,14 @@ struct Stack { int drop_vars_except(const VarDescrList& var_info, int excl_var = 0x80000000); void forget_var(var_idx_t idx); void push_new_var(var_idx_t idx); + void push_new_const(var_idx_t idx, const_idx_t cidx); void assign_var(var_idx_t new_idx, var_idx_t old_idx); void do_copy_var(var_idx_t new_idx, var_idx_t old_idx); void enforce_state(const StackLayout& req_stack); void rearrange_top(const StackLayout& top, std::vector last); void rearrange_top(var_idx_t top, bool last); + void merge_const(const Stack& req_stack); + void merge_state(const Stack& req_stack); void show(int _mode); void show() { show(mode); @@ -753,7 +1274,8 @@ struct Stack { /* * - * SPECIFIC SYMBOL VALUES + * SPECIFIC SYMBOL VALUES, + * BUILT-IN FUNCTIONS AND OPERATIONS * */ @@ -764,13 +1286,28 @@ inline simple_compile_func_t simple_compile(AsmOp op) { return [op](std::vector& out, std::vector& in) -> AsmOp { return op; }; } -struct SymValBuiltinFunc : SymVal { +struct SymValAsmFunc : SymValFunc { simple_compile_func_t compile; - ~SymValBuiltinFunc() override = default; - SymValBuiltinFunc(TypeExpr* _ft, const AsmOp& _macro) : SymVal(_Func, -1, _ft), compile(simple_compile(_macro)) { + ~SymValAsmFunc() override = default; + SymValAsmFunc(TypeExpr* ft, const AsmOp& _macro, bool impure = false) + : SymValFunc(-1, ft, impure), compile(simple_compile(_macro)) { + } + SymValAsmFunc(TypeExpr* ft, const simple_compile_func_t& _compile, bool impure = false) + : SymValFunc(-1, ft, impure), compile(_compile) { } - SymValBuiltinFunc(TypeExpr* _ft, const simple_compile_func_t& _compile) : SymVal(_Func, -1, _ft), compile(_compile) { + SymValAsmFunc(TypeExpr* ft, const simple_compile_func_t& _compile, std::initializer_list arg_order, + std::initializer_list ret_order = {}, bool impure = false) + : SymValFunc(-1, ft, arg_order, ret_order, impure), compile(_compile) { } }; +// defined in builtins.cpp +AsmOp exec_arg_op(std::string op, long long arg); +AsmOp exec_arg_op(std::string op, long long arg, int args, int retv = 1); +AsmOp exec_arg_op(std::string op, td::RefInt256 arg); +AsmOp exec_arg_op(std::string op, td::RefInt256 arg, int args, int retv = 1); +AsmOp push_const(td::RefInt256 x); + +void define_builtins(); + } // namespace funC diff --git a/ton-test-liteclient-full/lite-client/crypto/func/gen-abscode.cpp b/ton-test-liteclient-full/lite-client/crypto/func/gen-abscode.cpp new file mode 100644 index 0000000..2c29ac6 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/gen-abscode.cpp @@ -0,0 +1,307 @@ +#include "func.h" + +namespace funC { + +/* + * + * EXPRESSIONS + * + */ + +Expr* Expr::copy() const { + auto res = new Expr{*this}; + for (auto& arg : res->args) { + arg = arg->copy(); + } + return res; +} + +Expr::Expr(int c, sym_idx_t name_idx, std::initializer_list _arglist) : cls(c), args(std::move(_arglist)) { + sym = sym::lookup_symbol(name_idx); + if (!sym) { + } +} + +void Expr::chk_rvalue(const Lexem& lem) const { + if (!is_rvalue()) { + lem.error_at("rvalue expected before `", "`"); + } +} + +void Expr::chk_lvalue(const Lexem& lem) const { + if (!is_lvalue()) { + lem.error_at("lvalue expected before `", "`"); + } +} + +void Expr::chk_type(const Lexem& lem) const { + if (!is_type()) { + lem.error_at("type expression expected before `", "`"); + } +} + +bool Expr::deduce_type(const Lexem& lem) { + if (e_type) { + return true; + } + switch (cls) { + case _Apply: { + if (!sym) { + return false; + } + SymVal* sym_val = dynamic_cast(sym->value); + if (!sym_val || !sym_val->get_type()) { + return false; + } + std::vector arg_types; + for (const auto& arg : args) { + arg_types.push_back(arg->e_type); + } + TypeExpr* fun_type = TypeExpr::new_map(TypeExpr::new_tensor(arg_types), TypeExpr::new_hole()); + try { + uniformize(fun_type, sym_val->sym_type); + } catch (UniformizeError& ue) { + std::ostringstream os; + os << "cannot apply function " << sym->name() << " : " << sym_val->get_type() << " to arguments of type " + << fun_type->args[0] << ": " << ue; + lem.error(os.str()); + } + e_type = fun_type->args[1]; + TypeExpr::remove_indirect(e_type); + return true; + } + case _VarApply: { + assert(args.size() == 2); + TypeExpr* fun_type = TypeExpr::new_map(args[1]->e_type, TypeExpr::new_hole()); + try { + uniformize(fun_type, args[0]->e_type); + } catch (UniformizeError& ue) { + std::ostringstream os; + os << "cannot apply expression of type " << args[0]->e_type << " to an expression of type " << args[1]->e_type + << ": " << ue; + lem.error(os.str()); + } + e_type = fun_type->args[1]; + TypeExpr::remove_indirect(e_type); + return true; + } + case _Letop: { + assert(args.size() == 2); + try { + // std::cerr << "in assignment: " << args[0]->e_type << " from " << args[1]->e_type << std::endl; + uniformize(args[0]->e_type, args[1]->e_type); + } catch (UniformizeError& ue) { + std::ostringstream os; + os << "cannot assign an expression of type " << args[1]->e_type << " to a variable or pattern of type " + << args[0]->e_type << ": " << ue; + lem.error(os.str()); + } + e_type = args[0]->e_type; + TypeExpr::remove_indirect(e_type); + return true; + } + case _LetFirst: { + assert(args.size() == 2); + TypeExpr* rhs_type = TypeExpr::new_tensor({args[0]->e_type, TypeExpr::new_hole()}); + try { + // std::cerr << "in implicit assignment of a modifying method: " << rhs_type << " and " << args[1]->e_type << std::endl; + uniformize(rhs_type, args[1]->e_type); + } catch (UniformizeError& ue) { + std::ostringstream os; + os << "cannot implicitly assign an expression of type " << args[1]->e_type + << " to a variable or pattern of type " << rhs_type << " in modifying method `" << sym::symbols.get_name(val) + << "` : " << ue; + lem.error(os.str()); + } + e_type = rhs_type->args[1]; + TypeExpr::remove_indirect(e_type); + // std::cerr << "result type is " << e_type << std::endl; + return true; + } + case _CondExpr: { + assert(args.size() == 3); + auto flag_type = TypeExpr::new_atomic(_Int); + try { + uniformize(args[0]->e_type, flag_type); + } catch (UniformizeError& ue) { + std::ostringstream os; + os << "condition in a conditional expression has non-integer type " << args[0]->e_type << ": " << ue; + lem.error(os.str()); + } + try { + uniformize(args[1]->e_type, args[2]->e_type); + } catch (UniformizeError& ue) { + std::ostringstream os; + os << "the two variants in a conditional expression have different types " << args[1]->e_type << " and " + << args[2]->e_type << " : " << ue; + lem.error(os.str()); + } + e_type = args[1]->e_type; + TypeExpr::remove_indirect(e_type); + return true; + } + } + return false; +} + +int Expr::define_new_vars(CodeBlob& code) { + switch (cls) { + case _Tuple: + case _TypeApply: { + int res = 0; + for (const auto& x : args) { + res += x->define_new_vars(code); + } + return res; + } + case _Var: + if (val < 0) { + val = code.create_var(TmpVar::_Named, e_type, sym, &here); + return 1; + } + break; + case _Hole: + if (val < 0) { + val = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); + } + break; + } + return 0; +} + +int Expr::predefine_vars() { + switch (cls) { + case _Tuple: + case _TypeApply: { + int res = 0; + for (const auto& x : args) { + res += x->predefine_vars(); + } + return res; + } + case _Var: + if (!sym) { + assert(val < 0 && here.defined()); + sym = sym::define_symbol(~val, false, here); + if (!sym) { + throw src::ParseError{here, std::string{"redefined variable `"} + sym::symbols.get_name(~val) + "`"}; + } + sym->value = new SymVal{SymVal::_Var, -1, e_type}; + return 1; + } + break; + } + return 0; +} + +std::vector Expr::pre_compile(CodeBlob& code) const { + switch (cls) { + case _Tuple: { + std::vector res; + for (const auto& x : args) { + auto add = x->pre_compile(code); + res.insert(res.end(), add.cbegin(), add.cend()); + } + return res; + } + case _Apply: { + assert(sym); + std::vector res; + auto func = dynamic_cast(sym->value); + if (func && func->arg_order.size() == args.size()) { + //std::cerr << "!!! reordering " << args.size() << " arguments of " << sym->name() << std::endl; + std::vector> add_list(args.size()); + for (int i : func->arg_order) { + add_list[i] = args[i]->pre_compile(code); + } + for (const auto& add : add_list) { + res.insert(res.end(), add.cbegin(), add.cend()); + } + } else { + for (const auto& x : args) { + auto add = x->pre_compile(code); + res.insert(res.end(), add.cbegin(), add.cend()); + } + } + var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); + std::vector rvect{rv}; + auto& op = code.emplace_back(here, Op::_Call, rvect, std::move(res), sym); + if (flags & _IsImpure) { + op.flags |= Op::_Impure; + } + return rvect; + } + case _TypeApply: + return args[0]->pre_compile(code); + case _Var: + case _Hole: + return {val}; + case _VarApply: + if (args[0]->cls == _Glob) { + std::vector res = args[1]->pre_compile(code); + var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); + std::vector rvect{rv}; + auto& op = code.emplace_back(here, Op::_Call, rvect, std::move(res), args[0]->sym); + if (args[0]->flags & _IsImpure) { + op.flags |= Op::_Impure; + } + return rvect; + } else { + std::vector res = args[1]->pre_compile(code); + std::vector tfunc = args[0]->pre_compile(code); + if (tfunc.size() != 1) { + throw src::Fatal{"stack tuple used as a function"}; + } + res.push_back(tfunc[0]); + var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); + std::vector rvect{rv}; + code.emplace_back(here, Op::_CallInd, rvect, std::move(res)); + return rvect; + } + case _Const: { + var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); + std::vector rvect{rv}; + code.emplace_back(here, Op::_IntConst, rvect, intval); + return rvect; + } + case _Glob: { + var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); + std::vector rvect{rv}; + code.emplace_back(here, Op::_GlobVar, rvect, std::vector{}, sym); + return rvect; + } + case _Letop: { + std::vector right = args[1]->pre_compile(code); + std::vector left = args[0]->pre_compile(code); + code.emplace_back(here, Op::_Let, left, std::move(right)); + return left; + } + case _LetFirst: { + var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); + std::vector right = args[1]->pre_compile(code); + std::vector left = args[0]->pre_compile(code); + left.push_back(rv); + code.emplace_back(here, Op::_Let, std::move(left), std::move(right)); + return std::vector{rv}; + } + case _CondExpr: { + auto cond = args[0]->pre_compile(code); + assert(cond.size() == 1); + var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); + std::vector rvect{rv}; + Op& if_op = code.emplace_back(here, Op::_If, cond); + code.push_set_cur(if_op.block0); + code.emplace_back(here, Op::_Let, rvect, args[1]->pre_compile(code)); + code.close_pop_cur(args[1]->here); + code.push_set_cur(if_op.block1); + code.emplace_back(here, Op::_Let, rvect, args[2]->pre_compile(code)); + code.close_pop_cur(args[2]->here); + return rvect; + } + default: + std::cerr << "expression constructor is " << cls << std::endl; + throw src::Fatal{"cannot compile expression with unknown constructor"}; + } +} + +} // namespace funC diff --git a/ton-test-liteclient-full/lite-client/crypto/func/keywords.cpp b/ton-test-liteclient-full/lite-client/crypto/func/keywords.cpp new file mode 100644 index 0000000..cf97bae --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/keywords.cpp @@ -0,0 +1,92 @@ +#include "func.h" + +namespace src { + +int lexem_is_special(std::string str) { + return 0; // no special lexems +} + +} // namespace src + +namespace funC { + +/* + * + * KEYWORD DEFINITION + * + */ + +void define_keywords() { + sym::symbols.add_kw_char('+') + .add_kw_char('-') + .add_kw_char('*') + .add_kw_char('/') + .add_kw_char('%') + .add_kw_char('?') + .add_kw_char(':') + .add_kw_char(',') + .add_kw_char(';') + .add_kw_char('(') + .add_kw_char(')') + .add_kw_char('{') + .add_kw_char('}') + .add_kw_char('=') + .add_kw_char('_') + .add_kw_char('<') + .add_kw_char('>') + .add_kw_char('&') + .add_kw_char('|'); + + using Kw = funC::Keyword; + sym::symbols.add_keyword("==", Kw::_Eq) + .add_keyword("!=", Kw::_Neq) + .add_keyword("<=", Kw::_Leq) + .add_keyword(">=", Kw::_Geq) + .add_keyword("<=>", Kw::_Spaceship) + .add_keyword("<<", Kw::_Lshift) + .add_keyword(">>", Kw::_Rshift) + .add_keyword(">>~", Kw::_RshiftR) + .add_keyword(">>^", Kw::_RshiftC) + .add_keyword("/~", Kw::_DivR) + .add_keyword("/^", Kw::_DivC) + .add_keyword("/%", Kw::_DivMod) + .add_keyword("+=", Kw::_PlusLet) + .add_keyword("-=", Kw::_MinusLet) + .add_keyword("*=", Kw::_TimesLet) + .add_keyword("/=", Kw::_DivLet) + .add_keyword("%=", Kw::_ModLet) + .add_keyword("/~=", Kw::_DivRLet) + .add_keyword("/^=", Kw::_DivCLet) + .add_keyword("<<=", Kw::_LshiftLet) + .add_keyword(">>=", Kw::_RshiftLet) + .add_keyword(">>~=", Kw::_RshiftRLet) + .add_keyword(">>^=", Kw::_RshiftCLet); + + sym::symbols.add_keyword("return", Kw::_Return) + .add_keyword("var", Kw::_Var) + .add_keyword("repeat", Kw::_Repeat) + .add_keyword("do", Kw::_Do) + .add_keyword("while", Kw::_While) + .add_keyword("until", Kw::_Until) + .add_keyword("if", Kw::_If) + .add_keyword("then", Kw::_Then) + .add_keyword("else", Kw::_Else) + .add_keyword("elseif", Kw::_Elseif); + + sym::symbols.add_keyword("int", Kw::_Int) + .add_keyword("cell", Kw::_Cell) + .add_keyword("slice", Kw::_Slice) + .add_keyword("builder", Kw::_Builder) + .add_keyword("cont", Kw::_Cont) + .add_keyword("->", Kw::_Mapsto); + + sym::symbols.add_keyword("extern", Kw::_Extern) + .add_keyword("asm", Kw::_Asm) + .add_keyword("impure", Kw::_Impure) + .add_keyword("operator", Kw::_Operator) + .add_keyword("infix", Kw::_Infix) + .add_keyword("infixl", Kw::_Infixl) + .add_keyword("infixr", Kw::_Infixr); +} + +} // namespace funC diff --git a/ton-test-liteclient-full/lite-client/crypto/func/lexer.hpp b/ton-test-liteclient-full/lite-client/crypto/func/lexer.hpp deleted file mode 100644 index c0c1899..0000000 --- a/ton-test-liteclient-full/lite-client/crypto/func/lexer.hpp +++ /dev/null @@ -1,466 +0,0 @@ -namespace src { - -/* - * - * LEXER - * - */ - -int lexem_is_special(std::string str); // return 0 if no special lexems are needed -int compute_symbol_subclass(std::string str); // return 0 if unneeded - -struct Lexem { - enum { Undefined = -2, Eof = -1, Unknown = 0, Ident = 0, Number = 1, Special = 2 }; - int tp; - int val; - std::string str; - SrcLocation loc; - int classify(); - Lexem(std::string _str = "", const SrcLocation& _loc = {}, int _tp = Unknown, int _val = 0) - : tp(_tp), val(_val), str(_str), loc(_loc) { - classify(); - } - int set(std::string _str = "", const SrcLocation& _loc = {}, int _tp = Unknown, int _val = 0); - Lexem& clear(const SrcLocation& _loc = {}, int _tp = Unknown, int _val = 0) { - tp = _tp; - val = _val; - loc = _loc; - str = ""; - return *this; - } - bool valid() const { - return tp != Undefined; - } - std::string name_str() const; - void error(std::string _str) const { - throw ParseError{loc, _str}; - } - void error_at(std::string str1, std::string str2) const { - error(str1 + str + str2); - } - - static std::string lexem_name_str(int idx); -}; - -class Lexer { - SourceReader& src; - bool eof; - Lexem lexem, peek_lexem; - unsigned char char_class[128]; - std::array eol_cmt, cmt_op, cmt_cl; - enum cc { left_active = 2, right_active = 1, active = 3, allow_repeat = 4 }; - - public: - bool eof_found() const { - return eof; - } - Lexer(SourceReader& _src, bool init = false, std::string active_chars = ":;,()", std::string eol_cmts = ";;", - std::string open_cmts = "{-", std::string close_cmts = "-}"); - const Lexem& next(); - const Lexem& cur() const { - return lexem; - } - const Lexem& peek(); - int tp() const { - return lexem.tp; - } - void expect(int exp_tp, const char* msg = 0); - int classify_char(unsigned c) const { - return c < 0x80 ? char_class[c] : 0; - } - bool is_active(int c) const { - return (classify_char(c) & cc::active) == cc::active; - } - bool is_left_active(int c) const { - return (classify_char(c) & cc::left_active); - } - bool is_right_active(int c) const { - return (classify_char(c) & cc::right_active); - } - bool is_repeatable(int c) const { - return (classify_char(c) & cc::allow_repeat); - } - - private: - void set_spec(std::array& arr, std::string setup); -}; - -typedef int sym_idx_t; - -struct Symbol { - std::string str; - sym_idx_t idx; - int subclass; - Symbol(std::string _str, sym_idx_t _idx, int _sc) : str(_str), idx(_idx), subclass(_sc) { - } - Symbol(std::string _str, sym_idx_t _idx) : str(_str), idx(_idx) { - subclass = compute_symbol_subclass(std::move(_str)); - } -}; - -namespace { - -std::string unknown_symbol_name(sym_idx_t i) { - if (!i) { - return "_"; - } else { - std::ostringstream os; - os << "SYM#" << i; - return os.str(); - } -} - -} // namespace - -template -class SymTable { - public: - constexpr static int hprime = p; - constexpr static sym_idx_t not_found = 0; - constexpr static int max_kw_idx = 10000; - static int size() { - return p + 1; - } - - private: - sym_idx_t def_kw, def_sym; - std::unique_ptr sym[p + 1]; - sym_idx_t keywords[max_kw_idx]; - sym_idx_t gen_lookup(std::string str, int mode = 0, sym_idx_t idx = 0); - - public: - sym_idx_t lookup(std::string str, int mode = 0) { - return gen_lookup(str, mode); - } - sym_idx_t lookup_add(std::string str) { - return gen_lookup(str, 1); - } - SymTable& add_keyword(std::string str, sym_idx_t idx = 0); - SymTable& add_kw_char(char c) { - return add_keyword(std::string{c}, c); - } - Symbol* operator[](sym_idx_t i) const { - return sym[i].get(); - } - bool is_keyword(sym_idx_t i) const { - return sym[i] && sym[i]->idx < 0; - } - std::string get_name(sym_idx_t i) const { - return sym[i] ? sym[i]->str : unknown_symbol_name(i); - } - int get_subclass(sym_idx_t i) const { - return sym[i] ? sym[i]->subclass : 0; - } - Symbol* get_keyword(int i) const { - return ((unsigned)i < (unsigned)max_kw_idx) ? sym[keywords[i]].get() : nullptr; - } - SymTable() : def_kw(0x100), def_sym(0) { - std::memset(keywords, 0, sizeof(keywords)); - } -}; - -struct SymTableOverflow { - int sym_def; - SymTableOverflow(int x) : sym_def(x) { - } -}; - -struct SymTableKwRedef { - std::string kw; - SymTableKwRedef(std::string _kw) : kw(_kw) { - } -}; - -template -sym_idx_t SymTable

::gen_lookup(std::string str, int mode, sym_idx_t idx) { - unsigned long long h1 = 1, h2 = 1; - for (char c : str) { - h1 = ((h1 * 239) + (unsigned char)(c)) % p; - h2 = ((h2 * 17) + (unsigned char)(c)) % (p - 1); - } - ++h2; - ++h1; - while (true) { - if (sym[h1]) { - if (sym[h1]->str == str) { - return (mode & 2) ? not_found : sym_idx_t(h1); - } - h1 += h2; - if (h1 > p) { - h1 -= p; - } - } else { - if (!(mode & 1)) { - return not_found; - } - if (def_sym >= ((long)p * 3) / 4) { - throw SymTableOverflow{def_sym}; - } - sym[h1] = std::make_unique(str, idx <= 0 ? sym_idx_t(h1) : -idx); - ++def_sym; - return sym_idx_t(h1); - } - } -} - -template -SymTable

& SymTable

::add_keyword(std::string str, sym_idx_t idx) { - if (idx <= 0) { - idx = ++def_kw; - } - sym_idx_t res = gen_lookup(str, -1, idx); - if (!res) { - throw SymTableKwRedef{str}; - } - if (idx < max_kw_idx) { - keywords[idx] = res; - } - return *this; -} - -SymTable<100003> symbols; - -std::string Lexem::lexem_name_str(int idx) { - if (idx == Eof) { - return "end of file"; - } else if (idx == Ident) { - return "identifier"; - } else if (idx == Number) { - return "number"; - } else if (idx == Special) { - return "special"; - } else if (symbols.get_keyword(idx)) { - return "`" + symbols.get_keyword(idx)->str + "`"; - } else { - std::ostringstream os{""; - return os.str(); - } -} - -std::string Lexem::name_str() const { - if (tp == Ident) { - return std::string{"identifier `"} + symbols.get_name(val) + "`"; - } else { - return lexem_name_str(tp); - } -} - -bool is_number(std::string str) { - auto st = str.begin(), en = str.end(); - if (st == en) { - return false; - } - if (*st == '-') { - st++; - } - bool hex = false; - if (st + 1 < en && *st == '0' && st[1] == 'x') { - st += 2; - hex = true; - } - if (st == en) { - return false; - } - while (st < en) { - int c = *st; - if (c >= '0' && c <= '9') { - ++st; - continue; - } - if (!hex) { - return false; - } - c |= 0x20; - if (c < 'a' || c > 'f') { - return false; - } - } - return true; -} - -int Lexem::classify() { - if (tp != Unknown) { - return tp; - } - sym_idx_t i = symbols.lookup(str); - if (i) { - assert(str == symbols[i]->str); - str = symbols[i]->str; - sym_idx_t idx = symbols[i]->idx; - tp = (idx < 0 ? -idx : Ident); - val = i; - } else if (is_number(str)) { - tp = Number; - } else { - tp = lexem_is_special(str); - } - if (tp == Unknown) { - tp = Ident; - val = symbols.lookup(str, 1); - } - return tp; -} - -int Lexem::set(std::string _str, const SrcLocation& _loc, int _tp, int _val) { - str = _str; - loc = _loc; - tp = _tp; - val = _val; - return classify(); -} - -Lexer::Lexer(SourceReader& _src, bool init, std::string active_chars, std::string eol_cmts, std::string open_cmts, - std::string close_cmts) - : src(_src), eof(false), lexem("", src.here(), Lexem::Undefined), peek_lexem("", {}, Lexem::Undefined) { - memset(char_class, 0, sizeof(char_class)); - unsigned char activity = cc::active; - for (char c : active_chars) { - if (c == ' ') { - if (!--activity) { - activity = cc::allow_repeat; - } - } else if ((unsigned)c < 0x80) { - char_class[(unsigned)c] |= activity; - } - } - set_spec(eol_cmt, eol_cmts); - set_spec(cmt_op, open_cmts); - set_spec(cmt_cl, close_cmts); - if (init) { - next(); - } -} - -void Lexer::set_spec(std::array& arr, std::string setup) { - arr[0] = arr[1] = arr[2] = -0x100; - std::size_t n = setup.size(), i; - for (i = 0; i < n; i++) { - if (setup[i] == ' ') { - continue; - } - if (i == n - 1 || setup[i + 1] == ' ') { - arr[0] = setup[i]; - } else if (i == n - 2 || (i < n - 2 && setup[i + 2] == ' ')) { - arr[1] = setup[i]; - arr[2] = setup[++i]; - } else { - while (i < n && setup[i] != ' ') { - i++; - } - } - } -} - -void Lexer::expect(int exp_tp, const char* msg) { - if (tp() != exp_tp) { - throw ParseError{lexem.loc, (msg ? std::string{msg} : Lexem::lexem_name_str(exp_tp)) + " expected instead of " + - cur().name_str()}; - } - next(); -} - -const Lexem& Lexer::next() { - if (peek_lexem.valid()) { - lexem = std::move(peek_lexem); - peek_lexem.clear({}, Lexem::Undefined); - eof = (lexem.tp == Lexem::Eof); - return lexem; - } - if (eof) { - return lexem.clear(src.here(), Lexem::Eof); - } - long long comm = 1; - while (!src.seek_eof()) { - int cc = src.cur_char(), nc = src.next_char(); - if (cc == eol_cmt[0] || (cc == eol_cmt[1] && nc == eol_cmt[2])) { - src.load_line(); - } else if (cc == cmt_op[1] && nc == cmt_op[2]) { - src.advance(2); - comm = comm * 2 + 1; - } else if (cc == cmt_op[0]) { - src.advance(1); - comm *= 2; - } else if (comm == 1) { - break; - } else if (cc == cmt_cl[1] && nc == cmt_cl[2]) { - if (!(comm & 1)) { - src.error(std::string{"a `"} + (char)cmt_op[0] + "` comment closed by `" + (char)cmt_cl[1] + (char)cmt_cl[2] + - "`"); - } - comm >>= 1; - src.advance(2); - } else if (cc == cmt_cl[0]) { - if (!(comm & 1)) { - src.error(std::string{"a `"} + (char)cmt_op[1] + (char)cmt_op[2] + "` comment closed by `" + (char)cmt_cl[0] + - "`"); - } - comm >>= 1; - src.advance(1); - } else { - src.advance(1); - } - if (comm < 0) { - src.error("too many nested comments"); - } - } - if (src.seek_eof()) { - eof = true; - if (comm > 1) { - if (comm & 1) { - src.error(std::string{"`"} + (char)cmt_op[1] + (char)cmt_op[2] + "` comment extends past end of file"); - } else { - src.error(std::string{"`"} + (char)cmt_op[0] + "` comment extends past end of file"); - } - } - return lexem.clear(src.here(), Lexem::Eof); - } - char c = src.cur_char(); - const char* end = src.get_ptr(); - if (c == '`') { - ++end; - while (end < src.get_end_ptr() && *end != '`') { - ++end; - } - if (*end != '`') { - src.error("a `back-quoted` token extends past end of line"); - } - lexem.set(std::string{src.get_ptr() + 1, end}, src.here()); - src.set_ptr(end + 1); - return lexem; - } - int len = 0, pc = -0x100; - while (end < src.get_end_ptr()) { - c = *end; - bool repeated = (c == pc && is_repeatable(c)); - if (c == ' ' || c == 9 || (len && is_left_active(c) && !repeated)) { - break; - } - ++len; - ++end; - if (is_right_active(c) && !repeated) { - break; - } - pc = c; - } - lexem.set(std::string{src.get_ptr(), end}, src.here()); - src.set_ptr(end); - return lexem; -} - -const Lexem& Lexer::peek() { - if (peek_lexem.valid()) { - return peek_lexem; - } - if (eof) { - return lexem.clear(src.here(), Lexem::Eof); - } - Lexem keep = std::move(lexem); - next(); - peek_lexem = std::move(lexem); - lexem = std::move(keep); - eof = false; - return peek_lexem; -} - -} // namespace src diff --git a/ton-test-liteclient-full/lite-client/crypto/func/optimize.cpp b/ton-test-liteclient-full/lite-client/crypto/func/optimize.cpp new file mode 100644 index 0000000..0ae70b7 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/optimize.cpp @@ -0,0 +1,416 @@ +#include "func.h" + +namespace funC { + +/* + * + * PEEPHOLE OPTIMIZER + * + */ + +void Optimizer::set_code(AsmOpConsList code) { + code_ = std::move(code); + unpack(); +} + +void Optimizer::unpack() { + int i = 0, j = 0; + for (AsmOpCons *p = code_.get(); p && i < n; p = p->cdr.get(), ++j) { + if (p->car->is_very_custom()) { + break; + } + if (p->car->is_comment()) { + continue; + } + op_cons_[i] = p; + op_[i] = std::move(p->car); + offs_[i] = j; + ++i; + } + l_ = i; + indent_ = (i ? op_[0]->indent : 0); +} + +void Optimizer::pack() { + for (int i = 0; i < l_; i++) { + op_cons_[i]->car = std::move(op_[i]); + op_cons_[i] = nullptr; + } + l_ = 0; +} + +void Optimizer::apply() { + if (!p_ && !q_) { + return; + } + assert(p_ > 0 && p_ <= l_ && q_ >= 0 && q_ <= n && l_ <= n); + for (int i = p_; i < l_; i++) { + assert(op_[i]); + op_cons_[i]->car = std::move(op_[i]); + op_cons_[i] = nullptr; + } + for (int c = offs_[p_ - 1]; c >= 0; --c) { + code_ = std::move(code_->cdr); + } + for (int j = q_ - 1; j >= 0; j--) { + assert(oq_[j]); + oq_[j]->indent = indent_; + code_ = AsmOpCons::cons(std::move(oq_[j]), std::move(code_)); + } + l_ = 0; +} + +AsmOpConsList Optimizer::extract_code() { + pack(); + return std::move(code_); +} + +void Optimizer::show_head() const { + if (!debug_) { + return; + } + std::cerr << "optimizing"; + for (int i = 0; i < l_; i++) { + if (op_[i]) { + std::cerr << ' ' << *op_[i] << ' '; + } else { + std::cerr << " (null) "; + } + } + std::cerr << std::endl; +} + +void Optimizer::show_left() const { + if (!debug_) { + return; + } + std::cerr << "// *** rewriting"; + for (int i = 0; i < p_; i++) { + if (op_[i]) { + std::cerr << ' ' << *op_[i] << ' '; + } else { + std::cerr << " (null) "; + } + } +} + +void Optimizer::show_right() const { + if (!debug_) { + return; + } + std::cerr << "->"; + for (int i = 0; i < q_; i++) { + if (oq_[i]) { + std::cerr << ' ' << *oq_[i] << ' '; + } else { + std::cerr << " (null) "; + } + } + std::cerr << std::endl; +} + +bool Optimizer::say(std::string str) const { + if (debug_) { + std::cerr << str << std::endl; + } + return true; +} + +bool Optimizer::is_const_push_swap() const { + return l_ >= 3 && op_[0]->is_gconst() && op_[1]->is_push() && op_[1]->a >= 1 && op_[2]->is_swap(); +} + +// PUSHCONST c ; PUSH s(i+1) ; SWAP -> PUSH s(i) ; PUSHCONST c +bool Optimizer::rewrite_const_push_swap() { + p_ = 3; + q_ = 2; + show_left(); + oq_[1] = std::move(op_[0]); + oq_[0] = std::move(op_[1]); + (oq_[0]->a)--; + show_right(); + return true; +} + +bool Optimizer::simple_rewrite(int p, AsmOp&& new_op) { + assert(p > 0 && p <= l_); + p_ = p; + q_ = 1; + show_left(); + oq_[0] = std::move(op_[0]); + *oq_[0] = new_op; + show_right(); + return true; +} + +bool Optimizer::simple_rewrite_nop() { + assert(p_ > 0 && p_ <= l_); + q_ = 0; + show_left(); + show_right(); + return true; +} + +bool Optimizer::is_pred(const std::function& pred, int min_p) { + min_p = std::max(min_p, pb_); + for (int p = l2_; p >= min_p; p--) { + if (pred(tr_[p - 1])) { + p_ = p; + return true; + } + } + return false; +} + +bool Optimizer::is_same_as(const StackTransform& trans, int min_p) { + return is_pred([&trans](const auto& t) { return t >= trans; }, min_p); +} + +// s1 s3 XCHG ; s0 s2 XCHG -> 2SWAP +bool Optimizer::is_2swap() { + static const StackTransform t_2swap{2, 3, 0, 1, 4}; + return is_same_as(t_2swap); +} + +// s3 PUSH ; s3 PUSH -> 2OVER +bool Optimizer::is_2over() { + static const StackTransform t_2over{2, 3, 0}; + return is_same_as(t_2over); +} + +bool Optimizer::is_2dup() { + static const StackTransform t_2dup{0, 1, 0}; + return is_same_as(t_2dup); +} + +bool Optimizer::is_tuck() { + static const StackTransform t_tuck{0, 1, 0, 2}; + return is_same_as(t_tuck); +} + +bool Optimizer::is_2drop() { + static const StackTransform t_2drop{2}; + return is_same_as(t_2drop); +} + +bool Optimizer::is_rot() { + return is_pred([](const auto& t) { return t.is_rot(); }); +} + +bool Optimizer::is_rotrev() { + return is_pred([](const auto& t) { return t.is_rotrev(); }); +} + +bool Optimizer::is_nop() { + return is_pred([](const auto& t) { return t.is_id(); }, 1); +} + +bool Optimizer::is_xchg(int* i, int* j) { + return is_pred([i, j](const auto& t) { return t.is_xchg(i, j) && ((*i < 16 && *j < 16) || (!*i && *j < 256)); }); +} + +bool Optimizer::is_push(int* i) { + return is_pred([i](const auto& t) { return t.is_push(i) && *i < 256; }); +} + +bool Optimizer::is_pop(int* i) { + return is_pred([i](const auto& t) { return t.is_pop(i) && *i < 256; }); +} + +bool Optimizer::is_xchg2(int* i, int* j) { + return is_pred([i, j](const auto& t) { return t.is_xchg2(i, j) && *i < 16 && *j < 16; }); +} + +bool Optimizer::is_xcpu(int* i, int* j) { + return is_pred([i, j](const auto& t) { return t.is_xcpu(i, j) && *i < 16 && *j < 16; }); +} + +bool Optimizer::is_puxc(int* i, int* j) { + return is_pred([i, j](const auto& t) { return t.is_puxc(i, j) && *i < 16 && *j < 15; }); +} + +bool Optimizer::is_push2(int* i, int* j) { + return is_pred([i, j](const auto& t) { return t.is_push2(i, j) && *i < 16 && *j < 16; }); +} + +bool Optimizer::is_xchg3(int* i, int* j, int* k) { + return is_pred([i, j, k](const auto& t) { return t.is_xchg3(i, j, k) && *i < 16 && *j < 16 && *k < 16; }); +} + +bool Optimizer::is_xc2pu(int* i, int* j, int* k) { + return is_pred([i, j, k](const auto& t) { return t.is_xc2pu(i, j, k) && *i < 16 && *j < 16 && *k < 16; }); +} + +bool Optimizer::is_xcpuxc(int* i, int* j, int* k) { + return is_pred([i, j, k](const auto& t) { return t.is_xcpuxc(i, j, k) && *i < 16 && *j < 16 && *k < 15; }); +} + +bool Optimizer::is_xcpu2(int* i, int* j, int* k) { + return is_pred([i, j, k](const auto& t) { return t.is_xcpu2(i, j, k) && *i < 16 && *j < 16 && *k < 16; }); +} + +bool Optimizer::is_puxc2(int* i, int* j, int* k) { + return is_pred([i, j, k](const auto& t) { return t.is_puxc2(i, j, k) && *i < 16 && *j < 15 && *k < 15; }); +} + +bool Optimizer::is_puxcpu(int* i, int* j, int* k) { + return is_pred([i, j, k](const auto& t) { return t.is_puxcpu(i, j, k) && *i < 16 && *j < 15 && *k < 15; }); +} + +bool Optimizer::is_pu2xc(int* i, int* j, int* k) { + return is_pred([i, j, k](const auto& t) { return t.is_pu2xc(i, j, k) && *i < 16 && *j < 15 && *k < 14; }); +} + +bool Optimizer::is_push3(int* i, int* j, int* k) { + return is_pred([i, j, k](const auto& t) { return t.is_push3(i, j, k) && *i < 16 && *j < 16 && *k < 16; }); +} + +bool Optimizer::is_blkswap(int* i, int* j) { + return is_pred([i, j](const auto& t) { return t.is_blkswap(i, j) && *i > 0 && *j > 0 && *i <= 16 && *j <= 16; }); +} + +bool Optimizer::is_blkpush(int* i, int* j) { + return is_pred([i, j](const auto& t) { return t.is_blkpush(i, j) && *i > 0 && *i < 16 && *j < 16; }); +} + +bool Optimizer::is_blkdrop(int* i) { + return is_pred([i](const auto& t) { return t.is_blkdrop(i) && *i > 0 && *i < 16; }); +} + +bool Optimizer::is_reverse(int* i, int* j) { + return is_pred([i, j](const auto& t) { return t.is_reverse(i, j) && *i >= 2 && *i <= 17 && *j < 16; }); +} + +bool Optimizer::compute_stack_transforms() { + StackTransform trans; + for (int i = 0; i < l_; i++) { + if (!apply_op(trans, *op_[i])) { + l2_ = i; + return true; + } + tr_[i] = trans; + } + l2_ = l_; + return true; +} + +bool Optimizer::show_stack_transforms() const { + show_head(); + // slow version + /* + StackTransform trans2; + std::cerr << "id = " << trans2 << std::endl; + for (int i = 0; i < l_; i++) { + StackTransform op; + if (!apply_op(op, *op_[i])) { + std::cerr << "* (" << *op_[i] << " = invalid)\n"; + break; + } + trans2 *= op; + std::cerr << "* " << *op_[i] << " = " << op << " -> " << trans2 << std::endl; + } + */ + // fast version + StackTransform trans; + for (int i = 0; i < l_; i++) { + std::cerr << trans << std::endl << *op_[i] << " -> "; + if (!apply_op(trans, *op_[i])) { + std::cerr << " " << std::endl; + return true; + } + } + std::cerr << trans << std::endl; + return true; +} + +bool Optimizer::find_at_least(int pb) { + p_ = q_ = 0; + pb_ = pb; + // show_stack_transforms(); + int i = -100, j = -100, k = -100; + return (is_const_push_swap() && 3 >= pb && rewrite_const_push_swap()) || (is_nop() && simple_rewrite_nop()) || + (is_xchg(&i, &j) && simple_rewrite(AsmOp::Xchg(i, j))) || (is_push(&i) && simple_rewrite(AsmOp::Push(i))) || + (is_pop(&i) && simple_rewrite(AsmOp::Pop(i))) || (is_rot() && simple_rewrite(AsmOp::Custom("ROT", 3, 3))) || + (is_rotrev() && simple_rewrite(AsmOp::Custom("-ROT", 3, 3))) || + (is_2dup() && simple_rewrite(AsmOp::Custom("2DUP", 2, 4))) || + (is_2swap() && simple_rewrite(AsmOp::Custom("2SWAP", 2, 4))) || + (is_2over() && simple_rewrite(AsmOp::Custom("2OVER", 2, 4))) || + (is_tuck() && simple_rewrite(AsmOp::Custom("TUCK", 2, 3))) || + (is_2drop() && simple_rewrite(AsmOp::Custom("2DROP", 2, 0))) || + (is_xchg2(&i, &j) && simple_rewrite(AsmOp::Xchg2(i, j))) || + (is_xcpu(&i, &j) && simple_rewrite(AsmOp::XcPu(i, j))) || + (is_puxc(&i, &j) && simple_rewrite(AsmOp::PuXc(i, j))) || + (is_push2(&i, &j) && simple_rewrite(AsmOp::Push2(i, j))) || + (is_blkswap(&i, &j) && simple_rewrite(AsmOp::BlkSwap(i, j))) || + (is_blkpush(&i, &j) && simple_rewrite(AsmOp::BlkPush(i, j))) || + (is_blkdrop(&i) && simple_rewrite(AsmOp::BlkDrop(i))) || + (is_reverse(&i, &j) && simple_rewrite(AsmOp::BlkReverse(i, j))) || + (is_xchg3(&i, &j, &k) && simple_rewrite(AsmOp::Xchg3(i, j, k))) || + (is_xc2pu(&i, &j, &k) && simple_rewrite(AsmOp::Xc2Pu(i, j, k))) || + (is_xcpuxc(&i, &j, &k) && simple_rewrite(AsmOp::XcPuXc(i, j, k))) || + (is_xcpu2(&i, &j, &k) && simple_rewrite(AsmOp::XcPu2(i, j, k))) || + (is_puxc2(&i, &j, &k) && simple_rewrite(AsmOp::PuXc2(i, j, k))) || + (is_puxcpu(&i, &j, &k) && simple_rewrite(AsmOp::PuXcPu(i, j, k))) || + (is_pu2xc(&i, &j, &k) && simple_rewrite(AsmOp::Pu2Xc(i, j, k))) || + (is_push3(&i, &j, &k) && simple_rewrite(AsmOp::Push3(i, j, k))); +} + +bool Optimizer::find() { + if (!compute_stack_transforms()) { + return false; + } + for (int pb = l_; pb > 0; --pb) { + if (find_at_least(pb)) { + return true; + } + } + return false; +} + +bool Optimizer::optimize() { + bool f = false; + while (find()) { + f = true; + apply(); + unpack(); + } + return f; +} + +AsmOpConsList optimize_code_head(AsmOpConsList op_list) { + Optimizer opt(std::move(op_list), true /* debug */); + opt.optimize(); + return opt.extract_code(); +} + +AsmOpConsList optimize_code(AsmOpConsList op_list) { + std::vector> v; + while (op_list) { + if (!op_list->car->is_comment()) { + op_list = optimize_code_head(std::move(op_list)); + } + if (op_list) { + v.push_back(std::move(op_list->car)); + op_list = std::move(op_list->cdr); + } + } + for (auto it = v.rbegin(); it < v.rend(); ++it) { + op_list = AsmOpCons::cons(std::move(*it), std::move(op_list)); + } + return std::move(op_list); +} + +void optimize_code(AsmOpList& ops) { + std::unique_ptr op_list; + for (auto it = ops.list_.rbegin(); it < ops.list_.rend(); ++it) { + op_list = AsmOpCons::cons(std::make_unique(std::move(*it)), std::move(op_list)); + } + op_list = optimize_code(std::move(op_list)); + ops.list_.clear(); + while (op_list) { + ops.list_.push_back(std::move(*(op_list->car))); + op_list = std::move(op_list->cdr); + } +} + +} // namespace funC diff --git a/ton-test-liteclient-full/lite-client/crypto/func/parse-func.cpp b/ton-test-liteclient-full/lite-client/crypto/func/parse-func.cpp new file mode 100644 index 0000000..6aaa877 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/parse-func.cpp @@ -0,0 +1,1009 @@ +#include "func.h" +#include + +namespace sym { + +int compute_symbol_subclass(std::string str) { + using funC::IdSc; + if (str.size() < 2) { + return IdSc::undef; + } else if (str[0] == '.') { + return IdSc::dotid; + } else if (str[0] == '~') { + return IdSc::tildeid; + } else { + return IdSc::undef; + } +} + +} // namespace sym + +namespace funC { +using namespace std::literals::string_literals; +using src::Lexer; +using sym::symbols; +using td::Ref; + +inline bool is_dot_ident(sym_idx_t idx) { + return symbols.get_subclass(idx) == IdSc::dotid; +} + +inline bool is_tilde_ident(sym_idx_t idx) { + return symbols.get_subclass(idx) == IdSc::tildeid; +} + +inline bool is_special_ident(sym_idx_t idx) { + return symbols.get_subclass(idx) != IdSc::undef; +} + +/* + * + * PARSE SOURCE + * + */ + +// TE ::= TA | TA -> TE +// TA ::= int | ... | cont | var | _ | () | ( TE { , TE } ) +TypeExpr* parse_type(Lexer& lex); + +TypeExpr* parse_type1(Lexer& lex) { + switch (lex.tp()) { + case _Int: + lex.next(); + return TypeExpr::new_atomic(_Int); + case _Cell: + lex.next(); + return TypeExpr::new_atomic(_Cell); + case _Slice: + lex.next(); + return TypeExpr::new_atomic(_Slice); + case _Builder: + lex.next(); + return TypeExpr::new_atomic(_Builder); + case _Cont: + lex.next(); + return TypeExpr::new_atomic(_Cont); + case _Var: + case '_': + lex.next(); + return TypeExpr::new_hole(); + } + lex.expect('('); + if (lex.tp() == ')') { + lex.next(); + return TypeExpr::new_unit(); + } + auto t1 = parse_type(lex); + if (lex.tp() != ',') { + lex.expect(')'); + return t1; + } + std::vector tlist{1, t1}; + while (lex.tp() == ',') { + lex.next(); + tlist.push_back(parse_type(lex)); + } + lex.expect(')'); + return TypeExpr::new_tensor(std::move(tlist)); +} + +TypeExpr* parse_type(Lexer& lex) { + auto res = parse_type1(lex); + if (lex.tp() == _Mapsto) { + lex.next(); + auto to = parse_type(lex); + return TypeExpr::new_map(res, to); + } else { + return res; + } +} + +FormalArg parse_formal_arg(Lexer& lex, int fa_idx) { + TypeExpr* arg_type = 0; + SrcLocation loc = lex.cur().loc; + if (lex.tp() == '_') { + lex.next(); + if (lex.tp() == ',' || lex.tp() == ')') { + return std::make_tuple(TypeExpr::new_hole(), (SymDef*)nullptr, loc); + } + arg_type = TypeExpr::new_hole(); + loc = lex.cur().loc; + } else if (lex.tp() != _Ident) { + arg_type = parse_type(lex); + } else { + arg_type = TypeExpr::new_hole(); + } + if (lex.tp() == '_' || lex.tp() == ',' || lex.tp() == ')') { + if (lex.tp() == '_') { + loc = lex.cur().loc; + lex.next(); + } + return std::make_tuple(arg_type, (SymDef*)nullptr, loc); + } + if (lex.tp() != _Ident) { + lex.expect(_Ident, "formal parameter name"); + } + loc = lex.cur().loc; + SymDef* new_sym_def = sym::define_symbol(lex.cur().val, true, loc); + if (new_sym_def->value) { + lex.cur().error_at("redefined formal parameter `", "`"); + } + new_sym_def->value = new SymVal{SymVal::_Param, fa_idx, arg_type}; + lex.next(); + return std::make_tuple(arg_type, new_sym_def, loc); +} + +FormalArgList parse_formal_args(Lexer& lex) { + FormalArgList args; + lex.expect('(', "formal argument list"); + if (lex.tp() == ')') { + lex.next(); + return args; + } + int fa_idx = 0; + args.push_back(parse_formal_arg(lex, fa_idx++)); + while (lex.tp() == ',') { + lex.next(); + args.push_back(parse_formal_arg(lex, fa_idx++)); + } + lex.expect(')'); + return args; +} + +TypeExpr* extract_total_arg_type(const FormalArgList& arg_list) { + if (arg_list.empty()) { + return TypeExpr::new_unit(); + } + if (arg_list.size() == 1) { + return std::get<0>(arg_list[0]); + } + std::vector type_list; + for (auto& x : arg_list) { + type_list.push_back(std::get<0>(x)); + } + return TypeExpr::new_tensor(std::move(type_list)); +} + +SymValCodeFunc* make_new_glob_func(SymDef* func_sym, TypeExpr* func_type, bool impure = false) { + SymValCodeFunc* res = new SymValCodeFunc{glob_func_cnt, func_type, impure}; + func_sym->value = res; + glob_func.push_back(func_sym); + glob_func_cnt++; + return res; +} + +bool check_global_func(const Lexem& cur, sym_idx_t func_name = 0) { + if (!func_name) { + func_name = cur.val; + } + SymDef* def = sym::lookup_symbol(func_name); + if (!def) { + cur.loc.show_error(std::string{"undefined function `"} + symbols.get_name(func_name) + + "`, defining a global function of unknown type"); + def = sym::define_global_symbol(func_name, 0, cur.loc); + assert(def && "cannot define global function"); + ++undef_func_cnt; + make_new_glob_func(def, TypeExpr::new_hole()); // was: ... ::new_func() + return true; + } + SymVal* val = dynamic_cast(def->value); + if (!val) { + cur.error(std::string{"symbol `"} + symbols.get_name(func_name) + "` has no value and no type"); + return false; + } else if (!val->get_type()) { + cur.error(std::string{"symbol `"} + symbols.get_name(func_name) + "` has no type, possibly not a function"); + return false; + } else { + return true; + } +} + +Expr* parse_expr(Lexer& lex, CodeBlob& code, bool nv = false); + +// parse ( E { , E } ) | () | id | num | _ +Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) { + if (lex.tp() == '(') { + SrcLocation loc{lex.cur().loc}; + lex.next(); + if (lex.tp() == ')') { + lex.next(); + Expr* res = new Expr{Expr::_Tuple, {}}; + res->flags = Expr::_IsRvalue; + res->here = loc; + res->e_type = TypeExpr::new_unit(); + return res; + } + Expr* res = parse_expr(lex, code, nv); + if (lex.tp() != ',') { + lex.expect(')'); + return res; + } + std::vector type_list; + type_list.push_back(res->e_type); + int f = res->flags; + res = new Expr{Expr::_Tuple, {res}}; + while (lex.tp() == ',') { + lex.next(); + auto x = parse_expr(lex, code, nv); + res->pb_arg(x); + if ((f ^ x->flags) & Expr::_IsType) { + lex.cur().error("mixing type and non-type expressions inside the same tuple"); + } + f &= x->flags; + type_list.push_back(x->e_type); + } + res->here = loc; + res->flags = f; + res->e_type = TypeExpr::new_tensor(std::move(type_list)); + lex.expect(')'); + return res; + } + int t = lex.tp(); + if (t == Lexem::Number) { + Expr* res = new Expr{Expr::_Const, lex.cur().loc}; + res->flags = Expr::_IsRvalue; + td::RefInt256 val{true}; + if (!val.unique_write().parse_dec(lex.cur().str)) { + lex.cur().error_at("invalid integer constant `", "`"); + } + res->intval = std::move(val); + res->e_type = TypeExpr::new_atomic(_Int); + lex.next(); + return res; + } + if (t == '_') { + Expr* res = new Expr{Expr::_Hole, lex.cur().loc}; + res->val = -1; + res->flags = (Expr::_IsLvalue | Expr::_IsHole | Expr::_IsNewVar); + res->e_type = TypeExpr::new_hole(); + lex.next(); + return res; + } + if (t == _Var) { + Expr* res = new Expr{Expr::_Type, lex.cur().loc}; + res->flags = Expr::_IsType; + res->e_type = TypeExpr::new_hole(); + lex.next(); + return res; + } + if (t == _Int || t == _Cell || t == _Slice || t == _Builder || t == _Cont || t == _Type) { + Expr* res = new Expr{Expr::_Type, lex.cur().loc}; + res->flags = Expr::_IsType; + res->e_type = TypeExpr::new_atomic(t); + lex.next(); + return res; + } + if (t == _Ident) { + Expr* res = new Expr{Expr::_Var, lex.cur().loc}; + if (nv) { + res->val = ~lex.cur().val; + res->e_type = TypeExpr::new_hole(); + res->flags = Expr::_IsLvalue | Expr::_IsNewVar; + // std::cerr << "defined new variable " << lex.cur().str << " : " << res->e_type << std::endl; + } else { + res->sym = sym::lookup_symbol(lex.cur().val); + if (!res->sym) { + check_global_func(lex.cur()); + res->sym = sym::lookup_symbol(lex.cur().val); + } + SymVal* val = nullptr; + if (res->sym) { + val = dynamic_cast(res->sym->value); + } + if (!val) { + lex.cur().error_at("undefined identifier `", "`"); + } else if (val->type == SymVal::_Func) { + res->e_type = val->get_type(); + res->cls = Expr::_Glob; + } else if (val->idx < 0) { + lex.cur().error_at("accessing variable `", "` being defined"); + } else { + res->val = val->idx; + res->e_type = val->get_type(); + // std::cerr << "accessing variable " << lex.cur().str << " : " << res->e_type << std::endl; + } + // std::cerr << "accessing symbol " << lex.cur().str << " : " << res->e_type << (val->impure ? " (impure)" : " (pure)") << std::endl; + res->flags = Expr::_IsLvalue | Expr::_IsRvalue | (val->impure ? Expr::_IsImpure : 0); + } + res->deduce_type(lex.cur()); + lex.next(); + return res; + } + lex.expect(Lexem::Ident); + return nullptr; +} + +Expr* make_func_apply(Expr* fun, Expr* x) { + Expr* res; + if (fun->cls == Expr::_Glob) { + if (x->cls == Expr::_Tuple) { + res = new Expr{Expr::_Apply, fun->sym, x->args}; + } else { + res = new Expr{Expr::_Apply, fun->sym, {x}}; + } + res->flags = Expr::_IsRvalue | (fun->flags & Expr::_IsImpure); + } else { + res = new Expr{Expr::_VarApply, {fun, x}}; + res->flags = Expr::_IsRvalue; + } + return res; +} + +// parse E { E } +Expr* parse_expr90(Lexer& lex, CodeBlob& code, bool nv) { + Expr* res = parse_expr100(lex, code, nv); + while (lex.tp() == '(' || (lex.tp() == _Ident && !is_special_ident(lex.cur().val))) { + if (res->is_type()) { + Expr* x = parse_expr100(lex, code, true); + x->chk_lvalue(lex.cur()); // chk_lrvalue() ? + TypeExpr* tp = res->e_type; + delete res; + res = new Expr{Expr::_TypeApply, {x}}; + res->e_type = tp; + res->here = lex.cur().loc; + try { + uniformize(res->e_type, x->e_type); + } catch (UniformizeError& ue) { + std::ostringstream os; + os << "cannot transform expression of type " << x->e_type << " to explicitly requested type " << res->e_type + << ": " << ue; + lex.cur().error(os.str()); + } + res->flags = x->flags; + } else { + Expr* x = parse_expr100(lex, code, false); + x->chk_rvalue(lex.cur()); + res = make_func_apply(res, x); + res->here = lex.cur().loc; + res->deduce_type(lex.cur()); + } + } + return res; +} + +// parse E { .method E | ~method E } +Expr* parse_expr80(Lexer& lex, CodeBlob& code, bool nv) { + Expr* res = parse_expr90(lex, code, nv); + while (lex.tp() == _Ident && is_special_ident(lex.cur().val)) { + auto modify = is_tilde_ident(lex.cur().val); + auto obj = res; + if (modify) { + obj->chk_lvalue(lex.cur()); + } else { + obj->chk_rvalue(lex.cur()); + } + auto loc = lex.cur().loc; + auto name = lex.cur().val; + auto sym = sym::lookup_symbol(name); + if (!sym || !dynamic_cast(sym->value)) { + auto name1 = symbols.lookup(lex.cur().str.substr(1)); + if (name1) { + auto sym1 = sym::lookup_symbol(name1); + if (sym1 && dynamic_cast(sym1->value)) { + name = name1; + sym = sym1; + } + } + } + check_global_func(lex.cur(), name); + std::cerr << "using symbol `" << symbols.get_name(name) << "` for method call of " << lex.cur().str << std::endl; + sym = sym::lookup_symbol(name); + SymValFunc* val = sym ? dynamic_cast(sym->value) : nullptr; + if (!val) { + lex.cur().error_at("undefined method identifier `", "`"); + } + lex.next(); + auto x = parse_expr100(lex, code, false); + x->chk_rvalue(lex.cur()); + if (x->cls == Expr::_Tuple) { + res = new Expr{Expr::_Apply, name, {obj}}; + res->args.insert(res->args.end(), x->args.begin(), x->args.end()); + } else { + res = new Expr{Expr::_Apply, name, {obj, x}}; + } + res->here = loc; + res->flags = Expr::_IsRvalue | (val->impure ? Expr::_IsImpure : 0); + res->deduce_type(lex.cur()); + if (modify) { + // FIXME (use _LetFirst instead of _Letop) + auto tmp = res; + res = new Expr{Expr::_LetFirst, {obj->copy(), tmp}}; + res->here = loc; + res->flags = tmp->flags; + res->set_val(name); + res->deduce_type(lex.cur()); + } + } + return res; +} + +// parse E { (* | / | % | /% ) E } +Expr* parse_expr30(Lexer& lex, CodeBlob& code, bool nv) { + Expr* res = parse_expr80(lex, code, nv); + while (lex.tp() == '*' || lex.tp() == '/' || lex.tp() == '%' || lex.tp() == _DivMod || lex.tp() == _DivC || + lex.tp() == _DivR || lex.tp() == '&') { + res->chk_rvalue(lex.cur()); + int t = lex.tp(); + sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur().str + "_"); + SrcLocation loc{lex.cur().loc}; + check_global_func(lex.cur(), name); + lex.next(); + auto x = parse_expr80(lex, code, false); + x->chk_rvalue(lex.cur()); + res = new Expr{Expr::_Apply, name, {res, x}}; + res->here = loc; + res->set_val(t); + res->flags = Expr::_IsRvalue; + res->deduce_type(lex.cur()); + } + return res; +} + +// parse [-] E { (+ | - | `|` ) E } +Expr* parse_expr20(Lexer& lex, CodeBlob& code, bool nv) { + Expr* res; + int t = lex.tp(); + if (t == '-') { + sym_idx_t name = symbols.lookup_add("-_"); + check_global_func(lex.cur(), name); + SrcLocation loc{lex.cur().loc}; + lex.next(); + auto x = parse_expr30(lex, code, false); + x->chk_rvalue(lex.cur()); + res = new Expr{Expr::_Apply, name, {x}}; + res->here = loc; + res->set_val(t); + res->flags = Expr::_IsRvalue; + res->deduce_type(lex.cur()); + } else { + res = parse_expr30(lex, code, nv); + } + while (lex.tp() == '-' || lex.tp() == '+' || lex.tp() == '|') { + res->chk_rvalue(lex.cur()); + t = lex.tp(); + sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur().str + "_"); + check_global_func(lex.cur(), name); + SrcLocation loc{lex.cur().loc}; + lex.next(); + auto x = parse_expr30(lex, code, false); + x->chk_rvalue(lex.cur()); + res = new Expr{Expr::_Apply, name, {res, x}}; + res->here = loc; + res->set_val(t); + res->flags = Expr::_IsRvalue; + res->deduce_type(lex.cur()); + } + return res; +} + +// parse E { ( << | >> | >>~ | >>^ ) E } +Expr* parse_expr17(Lexer& lex, CodeBlob& code, bool nv) { + Expr* res = parse_expr20(lex, code, nv); + while (lex.tp() == _Lshift || lex.tp() == _Rshift || lex.tp() == _RshiftC || lex.tp() == _RshiftR) { + res->chk_rvalue(lex.cur()); + int t = lex.tp(); + sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur().str + "_"); + check_global_func(lex.cur(), name); + SrcLocation loc{lex.cur().loc}; + lex.next(); + auto x = parse_expr20(lex, code, false); + x->chk_rvalue(lex.cur()); + res = new Expr{Expr::_Apply, name, {res, x}}; + res->here = loc; + res->set_val(t); + res->flags = Expr::_IsRvalue; + res->deduce_type(lex.cur()); + } + return res; +} + +// parse E [ (== | < | > | <= | >= | != | <=> ) E ] +Expr* parse_expr15(Lexer& lex, CodeBlob& code, bool nv) { + Expr* res = parse_expr17(lex, code, nv); + if (lex.tp() == _Eq || lex.tp() == '<' || lex.tp() == '>' || lex.tp() == _Leq || lex.tp() == _Geq || + lex.tp() == _Neq || lex.tp() == _Spaceship) { + res->chk_rvalue(lex.cur()); + int t = lex.tp(); + sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur().str + "_"); + check_global_func(lex.cur(), name); + SrcLocation loc{lex.cur().loc}; + lex.next(); + auto x = parse_expr17(lex, code, false); + x->chk_rvalue(lex.cur()); + res = new Expr{Expr::_Apply, name, {res, x}}; + res->here = loc; + res->set_val(t); + res->flags = Expr::_IsRvalue; + res->deduce_type(lex.cur()); + } + return res; +} + +// parse E [ ? E : E ] +Expr* parse_expr13(Lexer& lex, CodeBlob& code, bool nv) { + Expr* res = parse_expr15(lex, code, nv); + if (lex.tp() == '?') { + res->chk_rvalue(lex.cur()); + SrcLocation loc{lex.cur().loc}; + lex.next(); + auto x = parse_expr(lex, code, false); + x->chk_rvalue(lex.cur()); + lex.expect(':'); + auto y = parse_expr13(lex, code, false); + y->chk_rvalue(lex.cur()); + res = new Expr{Expr::_CondExpr, {res, x, y}}; + res->here = loc; + res->flags = Expr::_IsRvalue; + res->deduce_type(lex.cur()); + } + return res; +} + +// parse LE1 (= | += | -= | ... ) E2 +Expr* parse_expr10(Lexer& lex, CodeBlob& code, bool nv) { + auto x = parse_expr13(lex, code, nv); + int t = lex.tp(); + if (t == _PlusLet || t == _MinusLet || t == _TimesLet || t == _DivLet || t == _DivRLet || t == _DivCLet || + t == _ModLet || t == _LshiftLet || t == _RshiftLet || t == _RshiftCLet || t == _RshiftRLet) { + x->chk_lvalue(lex.cur()); + x->chk_rvalue(lex.cur()); + sym_idx_t name = symbols.lookup_add(std::string{"^_"} + lex.cur().str + "_"); + check_global_func(lex.cur(), name); + SrcLocation loc{lex.cur().loc}; + lex.next(); + auto y = parse_expr10(lex, code, false); + y->chk_rvalue(lex.cur()); + Expr* z = new Expr{Expr::_Apply, name, {x, y}}; + z->here = loc; + z->set_val(t); + z->flags = Expr::_IsRvalue; + z->deduce_type(lex.cur()); + Expr* res = new Expr{Expr::_Letop, {x->copy(), z}}; + res->here = loc; + res->flags = (x->flags & ~Expr::_IsType) | Expr::_IsRvalue; + res->set_val(t); + res->deduce_type(lex.cur()); + return res; + } else if (t == '=') { + x->chk_lvalue(lex.cur()); + SrcLocation loc{lex.cur().loc}; + lex.next(); + auto y = parse_expr10(lex, code, false); + y->chk_rvalue(lex.cur()); + x->predefine_vars(); + x->define_new_vars(code); + Expr* res = new Expr{Expr::_Letop, {x, y}}; + res->here = loc; + res->flags = (x->flags & ~Expr::_IsType) | Expr::_IsRvalue; + res->set_val(t); + res->deduce_type(lex.cur()); + return res; + } else { + return x; + } +} + +Expr* parse_expr(Lexer& lex, CodeBlob& code, bool nv) { + return parse_expr10(lex, code, nv); +} + +namespace blk_fl { +enum { end = 1, ret = 2, empty = 4 }; +typedef int val; +constexpr val init = end | empty; +void combine(val& x, const val y) { + x |= y & ret; + x &= y | ~(end | empty); +} +void combine_parallel(val& x, const val y) { + x &= y | ~(ret | empty); + x |= y & end; +} +} // namespace blk_fl + +blk_fl::val parse_return_stmt(Lexer& lex, CodeBlob& code) { + auto expr = parse_expr(lex, code); + expr->chk_rvalue(lex.cur()); + try { + // std::cerr << "in return: "; + uniformize(expr->e_type, code.ret_type); + } catch (UniformizeError& ue) { + std::ostringstream os; + os << "previous function return type " << code.ret_type + << " cannot be unified with return statement expression type " << expr->e_type << ": " << ue; + lex.cur().error(os.str()); + } + std::vector tmp_vars = expr->pre_compile(code); + code.emplace_back(lex.cur().loc, Op::_Return, std::move(tmp_vars)); + lex.expect(';'); + return blk_fl::ret; +} + +blk_fl::val parse_implicit_ret_stmt(Lexer& lex, CodeBlob& code) { + auto ret_type = TypeExpr::new_unit(); + try { + // std::cerr << "in implicit return: "; + uniformize(ret_type, code.ret_type); + } catch (UniformizeError& ue) { + std::ostringstream os; + os << "previous function return type " << code.ret_type + << " cannot be unified with implicit end-of-block return type " << ret_type << ": " << ue; + lex.cur().error(os.str()); + } + code.emplace_back(lex.cur().loc, Op::_Return); + return blk_fl::ret; +} + +blk_fl::val parse_stmt(Lexer& lex, CodeBlob& code); + +blk_fl::val parse_block_stmt(Lexer& lex, CodeBlob& code) { + lex.expect('{'); + sym::open_scope(lex); + blk_fl::val res = blk_fl::init; + bool warned = false; + while (lex.tp() != '}') { + if (!(res & blk_fl::end) && !warned) { + lex.cur().loc.show_warning("unreachable code"); + warned = true; + } + blk_fl::combine(res, parse_stmt(lex, code)); + } + sym::close_scope(lex); + lex.expect('}'); + return res; +} + +blk_fl::val parse_repeat_stmt(Lexer& lex, CodeBlob& code) { + SrcLocation loc{lex.cur().loc}; + lex.expect(_Repeat); + auto expr = parse_expr(lex, code); + expr->chk_rvalue(lex.cur()); + auto cnt_type = TypeExpr::new_atomic(_Int); + try { + uniformize(expr->e_type, cnt_type); + } catch (UniformizeError& ue) { + std::ostringstream os; + os << "repeat count value of type " << expr->e_type << " is not an integer: " << ue; + lex.cur().error(os.str()); + } + std::vector tmp_vars = expr->pre_compile(code); + if (tmp_vars.size() != 1) { + lex.cur().error("repeat count value is not a singleton"); + } + Op& repeat_op = code.emplace_back(loc, Op::_Repeat, tmp_vars); + code.push_set_cur(repeat_op.block0); + blk_fl::val res = parse_block_stmt(lex, code); + code.close_pop_cur(lex.cur().loc); + return res | blk_fl::end; +} + +blk_fl::val parse_while_stmt(Lexer& lex, CodeBlob& code) { + SrcLocation loc{lex.cur().loc}; + lex.expect(_While); + auto expr = parse_expr(lex, code); + expr->chk_rvalue(lex.cur()); + auto cnt_type = TypeExpr::new_atomic(_Int); + try { + uniformize(expr->e_type, cnt_type); + } catch (UniformizeError& ue) { + std::ostringstream os; + os << "while condition value of type " << expr->e_type << " is not an integer: " << ue; + lex.cur().error(os.str()); + } + Op& while_op = code.emplace_back(loc, Op::_While); + code.push_set_cur(while_op.block0); + while_op.left = expr->pre_compile(code); + code.close_pop_cur(lex.cur().loc); + if (while_op.left.size() != 1) { + lex.cur().error("while condition value is not a singleton"); + } + code.push_set_cur(while_op.block1); + blk_fl::val res1 = parse_block_stmt(lex, code); + code.close_pop_cur(lex.cur().loc); + return res1 | blk_fl::end; +} + +blk_fl::val parse_do_stmt(Lexer& lex, CodeBlob& code) { + Op& while_op = code.emplace_back(lex.cur().loc, Op::_Until); + lex.expect(_Do); + code.push_set_cur(while_op.block0); + blk_fl::val res = parse_block_stmt(lex, code); + lex.expect(_Until); + auto expr = parse_expr(lex, code); + expr->chk_rvalue(lex.cur()); + auto cnt_type = TypeExpr::new_atomic(_Int); + try { + uniformize(expr->e_type, cnt_type); + } catch (UniformizeError& ue) { + std::ostringstream os; + os << "`until` condition value of type " << expr->e_type << " is not an integer: " << ue; + lex.cur().error(os.str()); + } + while_op.left = expr->pre_compile(code); + code.close_pop_cur(lex.cur().loc); + if (while_op.left.size() != 1) { + lex.cur().error("`until` condition value is not a singleton"); + } + return res & ~blk_fl::empty; +} + +blk_fl::val parse_if_stmt(Lexer& lex, CodeBlob& code, int first_lex = _If) { + SrcLocation loc{lex.cur().loc}; + lex.expect(first_lex); + auto expr = parse_expr(lex, code); + expr->chk_rvalue(lex.cur()); + auto flag_type = TypeExpr::new_atomic(_Int); + try { + uniformize(expr->e_type, flag_type); + } catch (UniformizeError& ue) { + std::ostringstream os; + os << "`if` condition value of type " << expr->e_type << " is not an integer: " << ue; + lex.cur().error(os.str()); + } + std::vector tmp_vars = expr->pre_compile(code); + if (tmp_vars.size() != 1) { + lex.cur().error("condition value is not a singleton"); + } + Op& if_op = code.emplace_back(loc, Op::_If, tmp_vars); + code.push_set_cur(if_op.block0); + blk_fl::val res1 = parse_block_stmt(lex, code); + blk_fl::val res2 = blk_fl::init; + code.close_pop_cur(lex.cur().loc); + if (lex.tp() == _Else) { + lex.expect(_Else); + code.push_set_cur(if_op.block1); + res2 = parse_block_stmt(lex, code); + code.close_pop_cur(lex.cur().loc); + } else if (lex.tp() == _Elseif) { + code.push_set_cur(if_op.block1); + res2 = parse_if_stmt(lex, code, _Elseif); + code.close_pop_cur(lex.cur().loc); + } + blk_fl::combine_parallel(res1, res2); + return res1; +} + +blk_fl::val parse_stmt(Lexer& lex, CodeBlob& code) { + switch (lex.tp()) { + case _Return: { + lex.next(); + return parse_return_stmt(lex, code); + } + case '{': { + return parse_block_stmt(lex, code); + } + case ';': { + lex.next(); + return blk_fl::init; + } + case _Repeat: + return parse_repeat_stmt(lex, code); + case _If: + return parse_if_stmt(lex, code); + case _Do: + return parse_do_stmt(lex, code); + case _While: + return parse_while_stmt(lex, code); + default: { + auto expr = parse_expr(lex, code); + expr->chk_rvalue(lex.cur()); + expr->pre_compile(code); + lex.expect(';'); + return blk_fl::end; + } + } +} + +CodeBlob* parse_func_body(Lexer& lex, FormalArgList arg_list, TypeExpr* ret_type) { + lex.expect('{'); + CodeBlob* blob = new CodeBlob{ret_type}; + blob->import_params(std::move(arg_list)); + blk_fl::val res = blk_fl::init; + bool warned = false; + while (lex.tp() != '}') { + if (!(res & blk_fl::end) && !warned) { + lex.cur().loc.show_warning("unreachable code"); + warned = true; + } + blk_fl::combine(res, parse_stmt(lex, *blob)); + } + if (res & blk_fl::end) { + parse_implicit_ret_stmt(lex, *blob); + } + blob->close_blk(lex.cur().loc); + lex.expect('}'); + return blob; +} + +SymValAsmFunc* parse_asm_func_body(Lexer& lex, TypeExpr* func_type, const FormalArgList& arg_list, TypeExpr* ret_type, + bool impure = false) { + auto loc = lex.cur().loc; + lex.expect(_Asm); + int cnt = (int)arg_list.size(); + int width = ret_type->get_width(); + if (width < 0 || width > 16) { + throw src::ParseError{loc, "return type of an assembler built-in function must have a well-defined fixed width"}; + } + if (arg_list.size() > 16) { + throw src::ParseError{loc, "assembler built-in function must have at most 16 arguments"}; + } + std::vector cum_arg_width; + cum_arg_width.push_back(0); + int tot_width = 0; + for (auto& arg : arg_list) { + int arg_width = std::get(arg)->get_width(); + if (arg_width < 0 || arg_width > 16) { + throw src::ParseError{std::get(arg), + "parameters of an assembler built-in function must have a well-defined fixed width"}; + } + cum_arg_width.push_back(tot_width += arg_width); + } + std::vector asm_ops; + std::vector arg_order, ret_order; + if (lex.tp() == '(') { + lex.expect('('); + if (lex.tp() != _Mapsto) { + std::vector visited(cnt, false); + for (int i = 0; i < cnt; i++) { + if (lex.tp() != _Ident) { + lex.expect(_Ident); + } + auto sym = sym::lookup_symbol(lex.cur().val); + int j; + for (j = 0; j < cnt; j++) { + if (std::get(arg_list[j]) == sym) { + break; + } + } + if (j == cnt) { + lex.cur().error("formal argument name expected"); + } + if (visited[j]) { + lex.cur().error("formal argument listed twice"); + } + visited[j] = true; + int c1 = cum_arg_width[j], c2 = cum_arg_width[j + 1]; + while (c1 < c2) { + arg_order.push_back(c1++); + } + lex.next(); + } + assert(arg_order.size() == (unsigned)tot_width); + } + if (lex.tp() == _Mapsto) { + lex.expect(_Mapsto); + std::vector visited(width, false); + for (int i = 0; i < width; i++) { + if (lex.tp() != Lexem::Number || lex.cur().str.size() > 3) { + lex.expect(Lexem::Number); + } + int j = atoi(lex.cur().str.c_str()); + if (j < 0 || j >= width || visited[j]) { + lex.cur().error("expected integer return value index 0 .. width-1"); + } + visited[j] = true; + ret_order.push_back(j); + lex.next(); + } + } + lex.expect(')'); + } + while (lex.tp() == _String) { + asm_ops.push_back(AsmOp::Custom(lex.cur().str, cnt, 1)); + lex.next(); + cnt = width; + } + if (asm_ops.empty()) { + throw src::ParseError{lex.cur().loc, "string with assembler instruction expected"}; + } + lex.expect(';'); + if (asm_ops.size() != 1) { + throw src::ParseError{loc, "assembler definition must consist of exactly one string with an assembler instruction"}; + } + auto res = new SymValAsmFunc{func_type, asm_ops[0], impure}; + res->arg_order = std::move(arg_order); + res->ret_order = std::move(ret_order); + return res; +} + +void parse_func_def(Lexer& lex) { + SrcLocation loc{lex.cur().loc}; + sym::open_scope(lex); + auto ret_type = parse_type(lex); + if (lex.tp() != _Ident) { + throw src::ParseError{lex.cur().loc, "function name identifier expected"}; + } + Lexem func_name = lex.cur(); + lex.next(); + FormalArgList arg_list = parse_formal_args(lex); + bool impure = (lex.tp() == _Impure); + if (impure) { + lex.next(); + } + if (lex.tp() != ';' && lex.tp() != '{' && lex.tp() != _Asm) { + lex.expect('{', "function body block expected"); + } + TypeExpr* func_type = TypeExpr::new_map(extract_total_arg_type(arg_list), ret_type); + std::cerr << "function " << func_name.str << " : " << func_type << std::endl; + SymDef* func_sym = sym::define_global_symbol(func_name.val, 0, loc); + assert(func_sym); + SymValFunc* func_sym_val = dynamic_cast(func_sym->value); + if (func_sym->value) { + if (func_sym->value->type != SymVal::_Func || !func_sym_val) { + lex.cur().error("was not defined as a function before"); + } + try { + uniformize(func_sym_val->sym_type, func_type); + } catch (UniformizeError& ue) { + std::ostringstream os; + os << "previous type of function " << func_name.str << " : " << func_sym_val->sym_type + << " cannot be unified with new type " << func_type << ": " << ue; + lex.cur().error(os.str()); + } + } + if (lex.tp() == ';') { + make_new_glob_func(func_sym, func_type, impure); + lex.next(); + } else if (lex.tp() == '{') { + if (dynamic_cast(func_sym_val)) { + lex.cur().error("function `"s + func_name.str + "` has been already defined as an assembler built-in"); + } + SymValCodeFunc* func_sym_code; + if (func_sym_val) { + func_sym_code = dynamic_cast(func_sym_val); + if (!func_sym_code) { + lex.cur().error("function `"s + func_name.str + "` has been already defined in an yet-unknown way"); + } + } else { + func_sym_code = make_new_glob_func(func_sym, func_type, impure); + } + if (func_sym_code->code) { + lex.cur().error("redefinition of function `"s + func_name.str + "`"); + } + CodeBlob* code = parse_func_body(lex, arg_list, ret_type); + code->name = func_name.str; + code->loc = loc; + // code->print(std::cerr); // !!!DEBUG!!! + func_sym_code->code = code; + } else { + if (func_sym_val) { + if (dynamic_cast(func_sym_val)) { + lex.cur().error("function `"s + func_name.str + "` was already declared as an ordinary function"); + } + if (dynamic_cast(func_sym_val)) { + lex.cur().error("redefinition of built-in assembler function `"s + func_name.str + "`"); + } + lex.cur().error("redefinition of previously (somehow) defined function `"s + func_name.str + "`"); + } + func_sym->value = parse_asm_func_body(lex, func_type, arg_list, ret_type, impure); + } + std::cerr << "new type of function " << func_name.str << " : " << func_type << std::endl; + sym::close_scope(lex); +} + +bool parse_source(std::istream* is, const src::FileDescr* fdescr) { + src::SourceReader reader{is, fdescr}; + Lexer lex{reader, true}; + while (lex.tp() != _Eof) { + parse_func_def(lex); + } + return true; +} + +bool parse_source_file(const char* filename) { + if (!filename || !*filename) { + throw src::Fatal{"source file name is an empty string"}; + } + src::FileDescr* cur_source = new src::FileDescr{filename}; + std::ifstream ifs{filename}; + if (ifs.fail()) { + throw src::Fatal{std::string{"cannot open source file `"} + filename + "`"}; + } + return parse_source(&ifs, cur_source); +} + +bool parse_source_stdin() { + return parse_source(&std::cin, new src::FileDescr{"stdin", true}); +} + +} // namespace funC diff --git a/ton-test-liteclient-full/lite-client/crypto/func/stack-transform.cpp b/ton-test-liteclient-full/lite-client/crypto/func/stack-transform.cpp new file mode 100644 index 0000000..2eb759f --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/stack-transform.cpp @@ -0,0 +1,787 @@ +#include "func.h" + +namespace funC { + +/* + * + * GENERIC STACK TRANSFORMATIONS + * + */ + +StackTransform::StackTransform(std::initializer_list list) { + *this = list; +} + +StackTransform &StackTransform::operator=(std::initializer_list list) { + if (list.size() > 255) { + invalidate(); + return *this; + } + set_id(); + if (!list.size()) { + return *this; + } + int m = (int)list.size(); + d = list.begin()[m - 1] - (m - 1); + if (d >= 128 || d < -128) { + invalidate(); + return *this; + } + for (int i = 0; i < m - 1; i++) { + int x = d + i; + int y = list.begin()[i]; + if (y != x) { + if (x != (short)x || y != (short)y || n == max_n) { + invalidate(); + return *this; + } + dp = std::max(dp, std::max(x, y) + 1); + A[n++] = std::make_pair((short)x, (short)y); + } + } + return *this; +} + +bool StackTransform::assign(const StackTransform &other) { + if (!other.is_valid() || (unsigned)other.n > max_n) { + return invalidate(); + } + d = other.d; + n = other.n; + dp = other.dp; + c = other.c; + invalid = false; + for (int i = 0; i < n; i++) { + A[i] = other.A[i]; + } + return true; +} + +int StackTransform::get(int x) const { + if (!is_valid()) { + return -1; + } + if (x <= c_start) { + return x - c; + } + x += d; + int i; + for (i = 0; i < n && A[i].first < x; i++) { + } + if (i < n && A[i].first == x) { + return A[i].second; + } else { + return x; + } +} + +bool StackTransform::set(int x, int y, bool relaxed) { + if (!is_valid()) { + return false; + } + if (x < 0) { + return (relaxed && y == x + d) || invalidate(); + } + if (!relaxed) { + touch(x); + } + x += d; + int i; + for (i = 0; i < n && A[i].first < x; i++) { + } + if (i < n && A[i].first == x) { + if (x != y) { + if (y != (short)y) { + return invalidate(); + } + A[i].second = (short)y; + } else { + --n; + for (; i < n; i++) { + A[i] = A[i + 1]; + } + } + } else { + if (x != y) { + if (x != (short)x || y != (short)y || n == max_n) { + return invalidate(); + } + for (int j = n++; j > i; j--) { + A[j] = A[j - 1]; + } + A[i].first = (short)x; + A[i].second = (short)y; + touch(x - d); + touch(y); + } + } + return true; +} + +// f(x') = x' + d for all x' >= x ? +bool StackTransform::is_trivial_after(int x) const { + return is_valid() && (!n || A[n - 1].first < x + d); +} + +// card f^{-1}(y) +int StackTransform::preimage_count(int y) const { + if (!is_valid()) { + return -1; + } + int count = (y >= d); + for (const auto &pair : A) { + if (pair.second == y) { + ++count; + } else if (pair.first == y) { + --count; + } + } + return count; +} + +// f^{-1}(y) +std::vector StackTransform::preimage(int y) const { + if (!is_valid()) { + return {}; + } + std::vector res; + bool f = (y >= d); + for (const auto &pair : A) { + if (pair.first > y && f) { + res.push_back(y - d); + f = false; + } + if (pair.first == y) { + f = false; + } else if (pair.second == y) { + res.push_back(pair.first - d); + } + } + return res; +} + +// is f:N->N bijective ? +bool StackTransform::is_permutation() const { + if (!is_valid() || d) { + return false; + } + assert(n <= max_n); + std::array X, Y; + for (int i = 0; i < n; i++) { + X[i] = A[i].first; + Y[i] = A[i].second; + if (Y[i] < 0) { + return false; + } + } + std::sort(Y.begin(), Y.begin() + n); + for (int i = 0; i < n; i++) { + if (X[i] != Y[i]) { + return false; + } + } + return true; +} + +bool StackTransform::remove_negative() { + int s = 0; + while (s < n && A[s].first < d) { + ++s; + } + if (s) { + n -= s; + for (int i = 0; i < n; i++) { + A[i] = A[i + s]; + } + } + return true; +} + +int StackTransform::try_load(int &i, int offs) const { + return i < n ? A[i++].first + offs : inf_x; +} + +bool StackTransform::try_store(int x, int y) { + if (x == y || x < d) { + return true; + } + if (n == max_n || x != (short)x || y != (short)y) { + return invalidate(); + } + A[n].first = (short)x; + A[n++].second = (short)y; + return true; +} + +// c := a * b +bool StackTransform::compose(const StackTransform &a, const StackTransform &b, StackTransform &c) { + if (!a.is_valid() || !b.is_valid()) { + return c.invalidate(); + } + c.d = a.d + b.d; + c.n = 0; + c.dp = std::max(a.dp, b.dp + a.d); + c.c = a.c + b.c; + c.invalid = false; + int i = 0, j = 0; + int x1 = a.try_load(i); + int x2 = b.try_load(j, a.d); + while (true) { + if (x1 < x2) { + int y = a.A[i - 1].second; + if (!c.try_store(x1, y)) { + return false; + } + x1 = a.try_load(i); + } else if (x2 < inf_x) { + if (x1 == x2) { + x1 = a.try_load(i); + } + int y = b.A[j - 1].second; + if (!c.try_store(x2, a(y))) { + return false; + } + x2 = b.try_load(j, a.d); + } else { + return true; + } + } +} + +// this = this * other +bool StackTransform::apply(const StackTransform &other) { + StackTransform res; + if (!compose(*this, other, res)) { + return invalidate(); + } + return assign(res); +} + +// this = other * this +bool StackTransform::preapply(const StackTransform &other) { + StackTransform res; + if (!compose(other, *this, res)) { + return invalidate(); + } + return assign(res); +} + +StackTransform StackTransform::operator*(const StackTransform &b) const & { + StackTransform res; + compose(*this, b, res); + return res; +} + +// this = this * other +StackTransform &StackTransform::operator*=(const StackTransform &other) { + StackTransform res; + (compose(*this, other, res) && assign(res)) || invalidate(); + return *this; +} + +bool StackTransform::apply_xchg(int i, int j, bool relaxed) { + if (!is_valid() || i < 0 || j < 0) { + return invalidate(); + } + if (i == j) { + return relaxed || touch(i); + } + int u = touch_get(i), v = touch_get(j); + return set(i, v) && set(j, u); +} + +bool StackTransform::apply_push(int i) { + if (!is_valid() || i < 0) { + return invalidate(); + } + int u = touch_get(i); + return shift(-1) && set(0, u); +} + +bool StackTransform::apply_push_newconst() { + if (!is_valid()) { + return false; + } + return shift(-1) && set(0, c_start - c++); +} + +bool StackTransform::apply_pop(int i) { + if (!is_valid() || i < 0) { + return invalidate(); + } + if (!i) { + return touch(0) && shift(1); + } else { + return set(i, get(0)) && shift(1); + } +} + +bool StackTransform::equal(const StackTransform &other, bool relaxed) const { + if (!is_valid() || !other.is_valid()) { + return false; + } + if (!(n == other.n && d == other.d)) { + return false; + } + for (int i = 0; i < n; i++) { + if (A[i] != other.A[i]) { + return false; + } + } + return relaxed || dp == other.dp; +} + +StackTransform StackTransform::Xchg(int i, int j, bool relaxed) { + StackTransform t; + t.apply_xchg(i, j, relaxed); + return t; +} + +StackTransform StackTransform::Push(int i) { + StackTransform t; + t.apply_push(i); + return t; +} + +StackTransform StackTransform::Pop(int i) { + StackTransform t; + t.apply_pop(i); + return t; +} + +bool StackTransform::is_xchg(int i, int j) const { + if (i == j) { + return is_id(); + } + return is_valid() && !d && n == 2 && i >= 0 && j >= 0 && get(i) == j && get(j) == i; +} + +bool StackTransform::is_xchg(int *i, int *j) const { + if (!is_valid() || d || n > 2 || !dp) { + return false; + } + if (!n) { + *i = *j = 0; + return true; + } + if (n != 2) { + return false; + } + int a = A[0].first, b = A[1].first; + if (A[0].second != b || A[1].second != a) { + return false; + } + *i = std::min(a, b); + *j = std::max(a, b); + return true; +} + +bool StackTransform::is_push(int i) const { + return is_valid() && d == -1 && n == 1 && A[0].first == -1 && A[0].second == i; +} + +bool StackTransform::is_push(int *i) const { + if (is_valid() && d == -1 && n == 1 && A[0].first == -1 && A[0].second >= 0) { + *i = A[0].second; + return true; + } else { + return false; + } +} + +// 1 2 3 4 .. = pop0 +// 0 2 3 4 .. = pop1 +// 1 0 3 4 .. = pop2 +// 1 2 0 4 .. = pop3 +bool StackTransform::is_pop(int i) const { + if (!is_valid() || d != 1 || n > 1 || i < 0) { + return false; + } + if (!i) { + return !n; + } + return n == 1 && A[0].first == i && !A[0].second; +} + +bool StackTransform::is_pop(int *i) const { + if (!is_valid() || d != 1 || n > 1) { + return false; + } + if (!n) { + *i = 0; + return true; + } + if (n == 1 && !A[0].second) { + *i = A[0].first; + return true; + } + return false; +} + +const StackTransform StackTransform::rot{2, 0, 1, 3}; +const StackTransform StackTransform::rot_rev{1, 2, 0, 3}; + +bool StackTransform::is_rot() const { + return equal(rot, true); +} + +bool StackTransform::is_rotrev() const { + return equal(rot_rev, true); +} + +// XCHG s1,s(i) ; XCHG s0,s(j) +bool StackTransform::is_xchg2(int i, int j) const { + StackTransform t; + return is_valid() && !d && t.apply_xchg(1, i) && t.apply_xchg(0, j) && t <= *this; +} + +bool StackTransform::is_xchg2(int *i, int *j) const { + if (!is_valid() || d || n > 4 || n == 1 || dp < 2) { + return false; + } + *i = get(1); + *j = get(0); + if (!n) { + return true; + } + if (*i < 0 || *j < 0) { + return false; + } + if (n != 3) { + return is_xchg2(*i, *j); + } + if (*i) { + // XCHG2 s(i),s(i) = XCHG s1,s(i) ; XCHG s0,s(i) : 0->1, 1->i + *j = *i; + } // XCHG2 s0,s(i) = XCHG s0,s1 ; XCHG s0,s(i) : 0->i, 1->0 + return is_xchg2(*i, *j); +} + +// XCHG s0,s(i) ; PUSH s(j) = PUSH s(j') ; XCHG s1,s(i+1) +// j'=j if j!=0, j!=i +// j'=0 if j=i +// j'=i if j=0 +bool StackTransform::is_xcpu(int i, int j) const { + StackTransform t; + return is_valid() && d == -1 && t.apply_xchg(0, i) && t.apply_push(j) && t <= *this; +} + +bool StackTransform::is_xcpu(int *i, int *j) const { + if (!is_valid() || d != -1 || n > 3 || dp < 1) { + return false; + } + *i = get(1); + *j = get(0); + if (!*j) { + *j = *i; + } else if (*j == *i) { + *j = 0; + } + return is_xcpu(*i, *j); +} + +// PUSH s(i) ; XCHG s0, s1 ; XCHG s0, s(j+1) +bool StackTransform::is_puxc(int i, int j) const { + StackTransform t; + return is_valid() && d == -1 && t.apply_push(i) && t.apply_xchg(0, 1) && t.apply_xchg(0, j + 1) && t <= *this; +} + +// j > 0 : 0 -> j, 1 -> i +// j = 0 : 0 -> i, 1 -> 0 ( PUSH s(i) ) +// j = -1 : 0 -> 0, 1 -> i ( PUSH s(i) ; XCHG s0, s1 ) +bool StackTransform::is_puxc(int *i, int *j) const { + if (!is_valid() || d != -1 || n > 3) { + return false; + } + *i = get(1); + *j = get(0); + if (!*i && is_push(*j)) { + std::swap(*i, *j); + return is_puxc(*i, *j); + } + if (!*j) { + --*j; + } + return is_puxc(*i, *j); +} + +// PUSH s(i) ; PUSH s(j+1) +bool StackTransform::is_push2(int i, int j) const { + StackTransform t; + return is_valid() && d == -2 && t.apply_push(i) && t.apply_push(j + 1) && t <= *this; +} + +bool StackTransform::is_push2(int *i, int *j) const { + if (!is_valid() || d != -2 || n > 2) { + return false; + } + *i = get(1); + *j = get(0); + return is_push2(*i, *j); +} + +// XCHG s2,s(i) ; XCHG s1,s(j) ; XCHG s0,s(k) +bool StackTransform::is_xchg3(int *i, int *j, int *k) const { + if (!is_valid() || d || dp < 3 || !is_permutation()) { + return false; + } + for (int s = 2; s >= 0; s--) { + *i = get(s); + StackTransform t = Xchg(2, *i) * *this; + if (t.is_xchg2(j, k)) { + return true; + } + } + return false; +} + +// XCHG s1,s(i) ; XCHG s0,s(j) ; PUSH s(k) +bool StackTransform::is_xc2pu(int *i, int *j, int *k) const { + if (!is_valid() || d != -1 || dp < 2) { + return false; + } + for (int s = 2; s >= 1; s--) { + *i = get(s); + StackTransform t = Xchg(1, *i) * *this; + if (t.is_xcpu(j, k)) { + return true; + } + } + return false; +} + +// XCHG s1,s(i) ; PUSH s(j) ; XCHG s0,s1 ; XCHG s0,s(k+1) +bool StackTransform::is_xcpuxc(int *i, int *j, int *k) const { + if (!is_valid() || d != -1 || dp < 2) { + return false; + } + for (int s = 2; s >= 0; s--) { + *i = get(s); + StackTransform t = Xchg(1, *i) * *this; + if (t.is_puxc(j, k)) { + return true; + } + } + return false; +} + +// XCHG s0,s(i) ; PUSH s(j) ; PUSH s(k+1) +bool StackTransform::is_xcpu2(int *i, int *j, int *k) const { + if (!is_valid() || d != -2 || dp < 1) { + return false; + } + *i = get(2); + StackTransform t = Xchg(0, *i) * *this; + return t.is_push2(j, k); +} + +// PUSH s(i) ; XCHG s0,s2 ; XCHG s1,s(j+1) ; XCHG s0,s(k+1) +// 0 -> i or 1 -> i or 2 -> i ; i has two preimages +// 0 -> k if k >= 2, k != j +// 1 -> j=k if j = k >= 2 +// 1 -> j if j >= 2, k != 0 +// 0 -> j if j >= 2, k = 0 +// => i in {f(0), f(1), f(2)} ; j in {-1, 0, 1, f(0), f(1)} ; k in {-1, 0, 1, f(0), f(1)} +bool StackTransform::is_puxc2(int *i, int *j, int *k) const { + if (!is_valid() || d != -1 || dp < 2) { + return false; + } + for (int s = 2; s >= 0; s--) { + *i = get(s); + if (preimage_count(*i) != 2) { + continue; + } + for (int u = -1; u <= 3; u++) { + *j = (u >= 2 ? get(u - 2) : u); + for (int v = -1; v <= 3; v++) { + *k = (v >= 2 ? get(v - 2) : v); + if (is_puxc2(*i, *j, *k)) { + return true; + } + } + } + } + return false; +} + +// PUSH s(i) ; XCHG s0,s2 ; XCHG s1,s(j+1) ; XCHG s0,s(k+1) +bool StackTransform::is_puxc2(int i, int j, int k) const { + StackTransform t; + return is_valid() && d == -1 && dp >= 2 // basic checks + && t.apply_push(i) && t.apply_xchg(0, 2) // PUSH s(i) ; XCHG s0,s2 + && t.apply_xchg(1, j + 1) // XCHG s1,s(j+1) + && t.apply_xchg(0, k + 1) && t <= *this; // XCHG s0,s(k+2) +} + +// PUSH s(i) ; XCHG s0,s1 ; XCHG s0,s(j+1) ; PUSH s(k+1) +bool StackTransform::is_puxcpu(int *i, int *j, int *k) const { + if (!is_valid() || d != -2 || dp < 1) { + return false; + } + StackTransform t = *this; + if (t.apply_pop() && t.is_puxc(i, j)) { + int y = get(0); + auto v = t.preimage(y); + if (!v.empty()) { + *k = v[0] - 1; + t.apply_push(*k + 1); + return t <= *this; + } + } + return false; +} + +// PUSH s(i) ; XCHG s0,s1 ; PUSH s(j+1) ; XCHG s0,s1 ; XCHG s0,s(k+2) +// 2 -> i; 1 -> j (if j >= 1, k != -1), 1 -> i (if j = 0, k != -1), 1 -> 0 (if j = -1, k != -1) +// 0 -> k (if k >= 1), 0 -> i (if k = 0), 0 -> j (if k = -1, j >= 1) +bool StackTransform::is_pu2xc(int *i, int *j, int *k) const { + if (!is_valid() || d != -2 || dp < 1) { + return false; + } + *i = get(2); + for (int v = -2; v <= 1; v++) { + *k = (v <= 0 ? v : get(0)); // one of -2, -1, 0, get(0) + for (int u = -1; u <= 1; u++) { + *j = (u <= 0 ? u : get(v != -1)); // one of -1, 0, get(0), get(1) + if (is_pu2xc(*i, *j, *k)) { + return true; + } + } + } + return false; +} + +bool StackTransform::is_pu2xc(int i, int j, int k) const { + StackTransform t; + return is_valid() && d == -2 && dp >= 1 // basic checks + && t.apply_push(i) && t.apply_xchg(0, 1) // PUSH s(i) ; XCHG s0,s1 + && t.apply_push(j + 1) && t.apply_xchg(0, 1) // PUSH s(j+1) ; XCHG s0,s1 + && t.apply_xchg(0, k + 2) && t <= *this; // XCHG s0,s(k+2) +} + +// PUSH s(i) ; PUSH s(j+1) ; PUSH s(k+2) +bool StackTransform::is_push3(int i, int j, int k) const { + StackTransform t; + return is_valid() && d == -3 && t.apply_push(i) && t.apply_push(j + 1) && t.apply_push(k + 2) && t <= *this; +} + +bool StackTransform::is_push3(int *i, int *j, int *k) const { + if (!is_valid() || d != -3 || n > 3) { + return false; + } + *i = get(2); + *j = get(1); + *k = get(0); + return is_push3(*i, *j, *k); +} + +bool StackTransform::is_blkswap(int *i, int *j) const { + if (!is_valid() || d || !is_permutation()) { + return false; + } + *j = get(0); + if (*j <= 0) { + return false; + } + auto v = preimage(0); + if (v.size() != 1) { + return false; + } + *i = v[0]; + return *i > 0 && is_blkswap(*i, *j); +} + +bool StackTransform::is_blkswap(int i, int j) const { + if (!is_valid() || d || i <= 0 || j <= 0 || dp < i + j || !is_trivial_after(i + j)) { + return false; + } + for (int s = 0; s < i; s++) { + if (get(s) != s + j) { + return false; + } + } + for (int s = 0; s < j; s++) { + if (get(s + i) != s) { + return false; + } + } + return true; +} + +// equivalent to i times DROP +bool StackTransform::is_blkdrop(int *i) const { + if (is_valid() && d > 0 && !n) { + *i = d; + return true; + } + return false; +} + +// equivalent to i times PUSH s(j) +bool StackTransform::is_blkpush(int *i, int *j) const { + if (!is_valid() || d >= 0) { + return false; + } + *i = -d; + *j = get(*i - 1); + return is_blkpush(*i, *j); +} + +bool StackTransform::is_blkpush(int i, int j) const { + if (!is_valid() || d >= 0 || d != -i || j < 0 || dp < i + j || !is_trivial_after(i)) { + return false; + } + StackTransform t; + for (int s = 0; s < i; s++) { + if (!t.apply_push(j)) { + return false; + } + } + return t <= *this; +} + +bool StackTransform::is_reverse(int *i, int *j) const { + if (!is_valid() || d || !is_permutation() || n < 2) { + return false; + } + *j = A[0].first; + *i = A[n - 1].first - A[0].first + 1; + return is_reverse(*i, *j); +} + +bool StackTransform::is_reverse(int i, int j) const { + if (!is_valid() || d || !is_trivial_after(i + j) || n < 2 || A[0].first != j || A[n - 1].first != j + i - 1) { + return false; + } + for (int s = 0; s < i; s++) { + if (get(j + s) != j + i - 1 - s) { + return false; + } + } + return true; +} + +void StackTransform::show(std::ostream &os, int mode) const { + if (!is_valid()) { + os << ""; + return; + } + int mi = 0, ma = 0; + if (n > 0 && A[0].first < d) { + mi = A[0].first - d; + } + if (n > 0) { + ma = std::max(ma, A[n - 1].first - d + 1); + } + ma = std::max(ma + 1, dp - d); + os << '{'; + if (dp == d) { + os << '|'; + } + for (int i = mi; i < ma; i++) { + os << get(i) << (i == -1 ? '?' : (i == dp - d - 1 ? '|' : ' ')); + } + os << get(ma) << "..}"; +} + +} // namespace funC diff --git a/ton-test-liteclient-full/lite-client/crypto/func/unify-types.cpp b/ton-test-liteclient-full/lite-client/crypto/func/unify-types.cpp new file mode 100644 index 0000000..83a6ab6 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/func/unify-types.cpp @@ -0,0 +1,276 @@ +#include "func.h" + +namespace funC { + +/* + * + * TYPE EXPRESSIONS + * + */ + +int TypeExpr::holes = 0; // not thread safe, but it is ok for now + +void TypeExpr::compute_width() { + switch (constr) { + case te_Atomic: + case te_Map: + minw = maxw = 1; + break; + case te_Tensor: + minw = maxw = 0; + for (TypeExpr* arg : args) { + minw += arg->minw; + maxw += arg->maxw; + } + if (minw > w_inf) { + minw = w_inf; + } + if (maxw > w_inf) { + maxw = w_inf; + } + break; + case te_Indirect: + minw = args[0]->minw; + maxw = args[0]->maxw; + break; + default: + minw = 0; + maxw = w_inf; + break; + } +} + +bool TypeExpr::recompute_width() { + switch (constr) { + case te_Tensor: + case te_Indirect: { + int min = 0, max = 0; + for (TypeExpr* arg : args) { + min += arg->minw; + max += arg->maxw; + } + if (min > maxw || max < minw) { + return false; + } + if (min > w_inf) { + min = w_inf; + } + if (max > w_inf) { + max = w_inf; + } + if (minw < min) { + minw = min; + } + if (maxw > max) { + maxw = max; + } + return true; + } + default: + return false; + } +} + +int TypeExpr::extract_components(std::vector& comp_list) { + if (constr != te_Indirect && constr != te_Tensor) { + comp_list.push_back(this); + return 1; + } + int res = 0; + for (TypeExpr* arg : args) { + res += arg->extract_components(comp_list); + } + return res; +} + +TypeExpr* TypeExpr::new_map(TypeExpr* from, TypeExpr* to) { + return new TypeExpr{te_Map, std::vector{from, to}}; +} + +void TypeExpr::replace_with(TypeExpr* te2) { + if (te2 == this) { + return; + } + constr = te_Indirect; + value = 0; + minw = te2->minw; + maxw = te2->maxw; + args.clear(); + args.push_back(te2); +} + +bool TypeExpr::remove_indirect(TypeExpr*& te, TypeExpr* forbidden) { + assert(te); + while (te->constr == te_Indirect) { + te = te->args[0]; + } + if (te->constr == te_Unknown) { + return te == forbidden; + } + bool res = false; + for (auto& x : te->args) { + res |= remove_indirect(x, forbidden); + } + return res; +} + +void TypeExpr::show_width(std::ostream& os) { + os << minw; + if (maxw != minw) { + os << ".."; + if (maxw < w_inf) { + os << maxw; + } + } +} + +std::ostream& operator<<(std::ostream& os, TypeExpr* type_expr) { + if (!type_expr) { + return os << "(null-type-ptr)"; + } + return type_expr->print(os); +} + +std::ostream& TypeExpr::print(std::ostream& os, int lex_level) { + switch (constr) { + case te_Unknown: + return os << "??" << value; + case te_Indirect: + return os << args[0]; + case te_Atomic: { + switch (value) { + case _Int: + return os << "int"; + case _Cell: + return os << "cell"; + case _Slice: + return os << "slice"; + case _Builder: + return os << "builder"; + case _Cont: + return os << "cont"; + case _Type: + return os << "type"; + default: + return os << "atomic-type-" << value; + } + } + case te_Tensor: { + os << "("; + auto c = args.size(); + if (c) { + for (const auto& x : args) { + x->print(os); + if (--c) { + os << ", "; + } + } + } + return os << ")"; + } + case te_Map: { + assert(args.size() == 2); + if (lex_level > 0) { + os << "("; + } + args[0]->print(os, 1); + os << " -> "; + args[1]->print(os); + if (lex_level > 0) { + os << ")"; + } + return os; + } + default: + return os << "unknown-type-expr-" << constr; + } +} + +void UniformizeError::print_message(std::ostream& os) const { + os << "cannot unify type " << te1 << " with " << te2; + if (!msg.empty()) { + os << ": " << msg; + } +} + +std::ostream& operator<<(std::ostream& os, const UniformizeError& ue) { + ue.print_message(os); + return os; +} + +std::string UniformizeError::message() const { + std::ostringstream os; + UniformizeError::print_message(os); + return os.str(); +} + +void check_width_compat(TypeExpr* te1, TypeExpr* te2) { + if (te1->minw > te2->maxw || te2->minw > te1->maxw) { + std::ostringstream os{"cannot uniformize types of widths "}; + te1->show_width(os); + os << " and "; + te2->show_width(os); + throw UniformizeError{te1, te2, os.str()}; + } +} + +void check_update_widths(TypeExpr* te1, TypeExpr* te2) { + check_width_compat(te1, te2); + te1->minw = te2->minw = std::max(te1->minw, te2->minw); + te1->maxw = te2->maxw = std::min(te1->maxw, te2->maxw); + assert(te1->minw <= te2->minw); +} + +void uniformize(TypeExpr*& te1, TypeExpr*& te2) { + assert(te1 && te2); + // std::cerr << "uniformize( " << te1 << " , " << te2 << " )\n"; + while (te1->constr == TypeExpr::te_Indirect) { + te1 = te1->args[0]; + } + while (te2->constr == TypeExpr::te_Indirect) { + te2 = te2->args[0]; + } + if (te1 == te2) { + return; + } + if (te1->constr == TypeExpr::te_Unknown) { + if (te2->constr == TypeExpr::te_Unknown) { + assert(te1->value != te2->value); + } + if (TypeExpr::remove_indirect(te2, te1)) { + throw UniformizeError{te1, te2, "uniformization results in an infinite cyclic type"}; + } + check_update_widths(te1, te2); + te1->replace_with(te2); + te1 = te2; + return; + } + if (te2->constr == TypeExpr::te_Unknown) { + if (TypeExpr::remove_indirect(te1, te2)) { + throw UniformizeError{te2, te1, "uniformization results in an infinite cyclic type"}; + } + check_update_widths(te2, te1); + te2->replace_with(te1); + te2 = te1; + return; + } + if (te1->constr != te2->constr || te1->value != te2->value || te1->args.size() != te2->args.size()) { + throw UniformizeError{te1, te2}; + } + for (std::size_t i = 0; i < te1->args.size(); i++) { + uniformize(te1->args[i], te2->args[i]); + } + if (te1->constr == TypeExpr::te_Tensor) { + if (!te1->recompute_width()) { + throw UniformizeError{te1, te2, "uniformization incompatible with known width of first type"}; + } + if (!te2->recompute_width()) { + throw UniformizeError{te2, te1, "uniformization incompatible with known width of first type"}; + } + check_update_widths(te1, te2); + } + te1->replace_with(te2); + te1 = te2; +} + +} // namespace funC diff --git a/ton-test-liteclient-full/lite-client/crypto/parser/lexer.cpp b/ton-test-liteclient-full/lite-client/crypto/parser/lexer.cpp new file mode 100644 index 0000000..2f9970b --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/parser/lexer.cpp @@ -0,0 +1,267 @@ +#include "lexer.h" +#include "symtable.h" +#include +#include + +namespace src { + +/* + * + * LEXER + * + */ + +std::string Lexem::lexem_name_str(int idx) { + if (idx == Eof) { + return "end of file"; + } else if (idx == Ident) { + return "identifier"; + } else if (idx == Number) { + return "number"; + } else if (idx == String) { + return "string"; + } else if (idx == Special) { + return "special"; + } else if (sym::symbols.get_keyword(idx)) { + return "`" + sym::symbols.get_keyword(idx)->str + "`"; + } else { + std::ostringstream os{""; + return os.str(); + } +} + +std::string Lexem::name_str() const { + if (tp == Ident) { + return std::string{"identifier `"} + sym::symbols.get_name(val) + "`"; + } else if (tp == String) { + return std::string{"string \""} + str + '"'; + } else { + return lexem_name_str(tp); + } +} + +bool is_number(std::string str) { + auto st = str.begin(), en = str.end(); + if (st == en) { + return false; + } + if (*st == '-') { + st++; + } + bool hex = false; + if (st + 1 < en && *st == '0' && st[1] == 'x') { + st += 2; + hex = true; + } + if (st == en) { + return false; + } + while (st < en) { + int c = *st; + if (c >= '0' && c <= '9') { + ++st; + continue; + } + if (!hex) { + return false; + } + c |= 0x20; + if (c < 'a' || c > 'f') { + return false; + } + } + return true; +} + +int Lexem::classify() { + if (tp != Unknown) { + return tp; + } + sym::sym_idx_t i = sym::symbols.lookup(str); + if (i) { + assert(str == sym::symbols[i]->str); + str = sym::symbols[i]->str; + sym::sym_idx_t idx = sym::symbols[i]->idx; + tp = (idx < 0 ? -idx : Ident); + val = i; + } else if (is_number(str)) { + tp = Number; + } else { + tp = lexem_is_special(str); + } + if (tp == Unknown) { + tp = Ident; + val = sym::symbols.lookup(str, 1); + } + return tp; +} + +int Lexem::set(std::string _str, const SrcLocation& _loc, int _tp, int _val) { + str = _str; + loc = _loc; + tp = _tp; + val = _val; + return classify(); +} + +Lexer::Lexer(SourceReader& _src, bool init, std::string active_chars, std::string eol_cmts, std::string open_cmts, + std::string close_cmts, std::string quote_chars) + : src(_src), eof(false), lexem("", src.here(), Lexem::Undefined), peek_lexem("", {}, Lexem::Undefined) { + memset(char_class, 0, sizeof(char_class)); + unsigned char activity = cc::active; + for (char c : active_chars) { + if (c == ' ') { + if (!--activity) { + activity = cc::allow_repeat; + } + } else if ((unsigned)c < 0x80) { + char_class[(unsigned)c] |= activity; + } + } + set_spec(eol_cmt, eol_cmts); + set_spec(cmt_op, open_cmts); + set_spec(cmt_cl, close_cmts); + for (int c : quote_chars) { + if (c > ' ' && c <= 0x7f) { + char_class[(unsigned)c] |= cc::quote_char; + } + } + if (init) { + next(); + } +} + +void Lexer::set_spec(std::array& arr, std::string setup) { + arr[0] = arr[1] = arr[2] = -0x100; + std::size_t n = setup.size(), i; + for (i = 0; i < n; i++) { + if (setup[i] == ' ') { + continue; + } + if (i == n - 1 || setup[i + 1] == ' ') { + arr[0] = setup[i]; + } else if (i == n - 2 || (i < n - 2 && setup[i + 2] == ' ')) { + arr[1] = setup[i]; + arr[2] = setup[++i]; + } else { + while (i < n && setup[i] != ' ') { + i++; + } + } + } +} + +void Lexer::expect(int exp_tp, const char* msg) { + if (tp() != exp_tp) { + throw ParseError{lexem.loc, (msg ? std::string{msg} : Lexem::lexem_name_str(exp_tp)) + " expected instead of " + + cur().name_str()}; + } + next(); +} + +const Lexem& Lexer::next() { + if (peek_lexem.valid()) { + lexem = std::move(peek_lexem); + peek_lexem.clear({}, Lexem::Undefined); + eof = (lexem.tp == Lexem::Eof); + return lexem; + } + if (eof) { + return lexem.clear(src.here(), Lexem::Eof); + } + long long comm = 1; + while (!src.seek_eof()) { + int cc = src.cur_char(), nc = src.next_char(); + if (cc == eol_cmt[0] || (cc == eol_cmt[1] && nc == eol_cmt[2])) { + src.load_line(); + } else if (cc == cmt_op[1] && nc == cmt_op[2]) { + src.advance(2); + comm = comm * 2 + 1; + } else if (cc == cmt_op[0]) { + src.advance(1); + comm *= 2; + } else if (comm == 1) { + break; + } else if (cc == cmt_cl[1] && nc == cmt_cl[2]) { + if (!(comm & 1)) { + src.error(std::string{"a `"} + (char)cmt_op[0] + "` comment closed by `" + (char)cmt_cl[1] + (char)cmt_cl[2] + + "`"); + } + comm >>= 1; + src.advance(2); + } else if (cc == cmt_cl[0]) { + if (!(comm & 1)) { + src.error(std::string{"a `"} + (char)cmt_op[1] + (char)cmt_op[2] + "` comment closed by `" + (char)cmt_cl[0] + + "`"); + } + comm >>= 1; + src.advance(1); + } else { + src.advance(1); + } + if (comm < 0) { + src.error("too many nested comments"); + } + } + if (src.seek_eof()) { + eof = true; + if (comm > 1) { + if (comm & 1) { + src.error(std::string{"`"} + (char)cmt_op[1] + (char)cmt_op[2] + "` comment extends past end of file"); + } else { + src.error(std::string{"`"} + (char)cmt_op[0] + "` comment extends past end of file"); + } + } + return lexem.clear(src.here(), Lexem::Eof); + } + int c = src.cur_char(); + const char* end = src.get_ptr(); + if (is_quote_char(c) || c == '`') { + int qc = c; + ++end; + while (end < src.get_end_ptr() && *end != qc) { + ++end; + } + if (*end != qc) { + src.error(qc == '`' ? "a `back-quoted` token extends past end of line" : "string extends past end of line"); + } + lexem.set(std::string{src.get_ptr() + 1, end}, src.here(), qc == '`' ? Lexem::Unknown : Lexem::String); + src.set_ptr(end + 1); + return lexem; + } + int len = 0, pc = -0x100; + while (end < src.get_end_ptr()) { + c = *end; + bool repeated = (c == pc && is_repeatable(c)); + if (c == ' ' || c == 9 || (len && is_left_active(c) && !repeated)) { + break; + } + ++len; + ++end; + if (is_right_active(c) && !repeated) { + break; + } + pc = c; + } + lexem.set(std::string{src.get_ptr(), end}, src.here()); + src.set_ptr(end); + return lexem; +} + +const Lexem& Lexer::peek() { + if (peek_lexem.valid()) { + return peek_lexem; + } + if (eof) { + return lexem.clear(src.here(), Lexem::Eof); + } + Lexem keep = std::move(lexem); + next(); + peek_lexem = std::move(lexem); + lexem = std::move(keep); + eof = false; + return peek_lexem; +} + +} // namespace src diff --git a/ton-test-liteclient-full/lite-client/crypto/parser/lexer.h b/ton-test-liteclient-full/lite-client/crypto/parser/lexer.h new file mode 100644 index 0000000..aac1fd0 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/parser/lexer.h @@ -0,0 +1,96 @@ +#pragma once +#include "srcread.h" +#include +#include +#include + +namespace src { + +/* + * + * LEXER + * + */ + +int lexem_is_special(std::string str); // return 0 if no special lexems are needed + +struct Lexem { + enum { Undefined = -2, Eof = -1, Unknown = 0, Ident = 0, Number = 1, Special = 2, String = 3 }; + int tp; + int val; + std::string str; + SrcLocation loc; + int classify(); + Lexem(std::string _str = "", const SrcLocation& _loc = {}, int _tp = Unknown, int _val = 0) + : tp(_tp), val(_val), str(_str), loc(_loc) { + classify(); + } + int set(std::string _str = "", const SrcLocation& _loc = {}, int _tp = Unknown, int _val = 0); + Lexem& clear(const SrcLocation& _loc = {}, int _tp = Unknown, int _val = 0) { + tp = _tp; + val = _val; + loc = _loc; + str = ""; + return *this; + } + bool valid() const { + return tp != Undefined; + } + std::string name_str() const; + void error(std::string _str) const { + throw ParseError{loc, _str}; + } + void error_at(std::string str1, std::string str2) const { + error(str1 + str + str2); + } + + static std::string lexem_name_str(int idx); +}; + +class Lexer { + SourceReader& src; + bool eof; + Lexem lexem, peek_lexem; + unsigned char char_class[128]; + std::array eol_cmt, cmt_op, cmt_cl; + enum cc { left_active = 2, right_active = 1, active = 3, allow_repeat = 4, quote_char = 8 }; + + public: + bool eof_found() const { + return eof; + } + Lexer(SourceReader& _src, bool init = false, std::string active_chars = ":;,() ~.", std::string eol_cmts = ";;", + std::string open_cmts = "{-", std::string close_cmts = "-}", std::string quote_chars = "\""); + const Lexem& next(); + const Lexem& cur() const { + return lexem; + } + const Lexem& peek(); + int tp() const { + return lexem.tp; + } + void expect(int exp_tp, const char* msg = 0); + int classify_char(unsigned c) const { + return c < 0x80 ? char_class[c] : 0; + } + bool is_active(int c) const { + return (classify_char(c) & cc::active) == cc::active; + } + bool is_left_active(int c) const { + return (classify_char(c) & cc::left_active); + } + bool is_right_active(int c) const { + return (classify_char(c) & cc::right_active); + } + bool is_repeatable(int c) const { + return (classify_char(c) & cc::allow_repeat); + } + bool is_quote_char(int c) const { + return (classify_char(c) & cc::quote_char); + } + + private: + void set_spec(std::array& arr, std::string setup); +}; + +} // namespace src diff --git a/ton-test-liteclient-full/lite-client/crypto/func/srcread.hpp b/ton-test-liteclient-full/lite-client/crypto/parser/srcread.cpp similarity index 56% rename from ton-test-liteclient-full/lite-client/crypto/func/srcread.hpp rename to ton-test-liteclient-full/lite-client/crypto/parser/srcread.cpp index 11798b0..160d9df 100644 --- a/ton-test-liteclient-full/lite-client/crypto/func/srcread.hpp +++ b/ton-test-liteclient-full/lite-client/crypto/parser/srcread.cpp @@ -1,3 +1,5 @@ +#include "srcread.h" + namespace src { /* @@ -6,56 +8,14 @@ namespace src { * */ -struct FileDescr { - std::string filename; - bool is_stdin; - FileDescr(std::string _fname, bool _stdin = false) : filename(std::move(_fname)), is_stdin(_stdin) { - } -}; - std::ostream& operator<<(std::ostream& os, const FileDescr* fdescr) { return os << (fdescr ? (fdescr->is_stdin ? "stdin" : fdescr->filename) : "unknown-location"); } -struct Fatal { - std::string message; - Fatal(std::string _msg) : message(std::move(_msg)) { - } - std::string get_msg() const { - return message; - } -}; - std::ostream& operator<<(std::ostream& os, const Fatal& fatal) { return os << fatal.get_msg(); } -struct SrcLocation { - const FileDescr* fdescr; - int line_no; - int line_pos; - std::string text; - SrcLocation() : fdescr(nullptr), line_no(0), line_pos(-1) { - } - SrcLocation(const FileDescr* _fdescr, int line = 0, int pos = -1) : fdescr(_fdescr), line_no(line), line_pos(pos) { - } - bool defined() const { - return fdescr; - } - void show(std::ostream& os) const; - bool show_context(std::ostream& os) const; - void show_gen_error(std::ostream& os, std::string message, std::string err_type = "") const; - void show_note(std::string err_msg) const { - show_gen_error(std::cerr, err_msg, "note"); - } - void show_warning(std::string err_msg) const { - show_gen_error(std::cerr, err_msg, "warning"); - } - void show_error(std::string err_msg) const { - show_gen_error(std::cerr, err_msg, "error"); - } -}; - void SrcLocation::show(std::ostream& os) const { os << fdescr; if (line_no > 0) { @@ -111,86 +71,16 @@ void SrcLocation::show_gen_error(std::ostream& os, std::string message, std::str show_context(os); } -struct Error { - virtual ~Error() = default; - virtual void show(std::ostream& os) const = 0; -}; - std::ostream& operator<<(std::ostream& os, const Error& error) { error.show(os); return os; } -struct ParseError : Error { - SrcLocation where; - std::string message; - ParseError(const SrcLocation& _where, std::string _msg) : where(_where), message(_msg) { - } - ParseError(const SrcLocation* _where, std::string _msg) : message(_msg) { - if (_where) { - where = *_where; - } - } - ~ParseError() override = default; - void show(std::ostream& os) const override; -}; - void ParseError::show(std::ostream& os) const { os << where << ": error: " << message << std::endl; where.show_context(os); } -class SourceReader { - std::istream* ifs; - SrcLocation loc; - bool eof; - std::string cur_line; - int cur_line_len; - void set_eof(); - const char *start, *cur, *end; - - public: - SourceReader(std::istream* _is, const FileDescr* _fdescr); - bool load_line(); - bool is_eof() const { - return eof; - } - int is_eoln() const { - return cur == end; - } - int skip_spc(); - bool seek_eoln() { - skip_spc(); - return is_eoln(); - } - bool seek_eof(); - const char* cur_line_cstr() const { - return cur_line.c_str(); - } - const SrcLocation& here() const { - return loc; - } - char cur_char() const { - return *cur; - } - char next_char() const { - return cur[1]; - } - const char* get_ptr() const { - return cur; - } - const char* get_end_ptr() const { - return end; - } - const char* set_ptr(const char* ptr); - void advance(int n) { - set_ptr(get_ptr() + n); - } - void error(std::string err_msg) { - throw ParseError{loc, err_msg}; - } -}; - SourceReader::SourceReader(std::istream* _is, const FileDescr* _fdescr) : ifs(_is), loc(_fdescr), eof(false), cur_line_len(0), start(0), cur(0), end(0) { load_line(); diff --git a/ton-test-liteclient-full/lite-client/crypto/parser/srcread.h b/ton-test-liteclient-full/lite-client/crypto/parser/srcread.h new file mode 100644 index 0000000..6faf635 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/parser/srcread.h @@ -0,0 +1,132 @@ +#pragma once + +#include +#include + +namespace src { + +/* + * + * SOURCE FILE READER + * + */ + +struct FileDescr { + std::string filename; + bool is_stdin; + FileDescr(std::string _fname, bool _stdin = false) : filename(std::move(_fname)), is_stdin(_stdin) { + } +}; + +struct Fatal { + std::string message; + Fatal(std::string _msg) : message(std::move(_msg)) { + } + std::string get_msg() const { + return message; + } +}; + +std::ostream& operator<<(std::ostream& os, const Fatal& fatal); + +struct SrcLocation { + const FileDescr* fdescr; + int line_no; + int line_pos; + std::string text; + SrcLocation() : fdescr(nullptr), line_no(0), line_pos(-1) { + } + SrcLocation(const FileDescr* _fdescr, int line = 0, int pos = -1) : fdescr(_fdescr), line_no(line), line_pos(pos) { + } + bool defined() const { + return fdescr; + } + void show(std::ostream& os) const; + bool show_context(std::ostream& os) const; + void show_gen_error(std::ostream& os, std::string message, std::string err_type = "") const; + void show_note(std::string err_msg) const { + show_gen_error(std::cerr, err_msg, "note"); + } + void show_warning(std::string err_msg) const { + show_gen_error(std::cerr, err_msg, "warning"); + } + void show_error(std::string err_msg) const { + show_gen_error(std::cerr, err_msg, "error"); + } +}; + +std::ostream& operator<<(std::ostream& os, const SrcLocation& loc); + +struct Error { + virtual ~Error() = default; + virtual void show(std::ostream& os) const = 0; +}; + +std::ostream& operator<<(std::ostream& os, const Error& error); + +struct ParseError : Error { + SrcLocation where; + std::string message; + ParseError(const SrcLocation& _where, std::string _msg) : where(_where), message(_msg) { + } + ParseError(const SrcLocation* _where, std::string _msg) : message(_msg) { + if (_where) { + where = *_where; + } + } + ~ParseError() override = default; + void show(std::ostream& os) const override; +}; + +class SourceReader { + std::istream* ifs; + SrcLocation loc; + bool eof; + std::string cur_line; + int cur_line_len; + void set_eof(); + const char *start, *cur, *end; + + public: + SourceReader(std::istream* _is, const FileDescr* _fdescr); + bool load_line(); + bool is_eof() const { + return eof; + } + int is_eoln() const { + return cur == end; + } + int skip_spc(); + bool seek_eoln() { + skip_spc(); + return is_eoln(); + } + bool seek_eof(); + const char* cur_line_cstr() const { + return cur_line.c_str(); + } + const SrcLocation& here() const { + return loc; + } + char cur_char() const { + return *cur; + } + char next_char() const { + return cur[1]; + } + const char* get_ptr() const { + return cur; + } + const char* get_end_ptr() const { + return end; + } + const char* set_ptr(const char* ptr); + void advance(int n) { + set_ptr(get_ptr() + n); + } + void error(std::string err_msg) { + throw ParseError{loc, err_msg}; + } +}; + +} // namespace src diff --git a/ton-test-liteclient-full/lite-client/crypto/func/symtable.hpp b/ton-test-liteclient-full/lite-client/crypto/parser/symtable.cpp similarity index 59% rename from ton-test-liteclient-full/lite-client/crypto/func/symtable.hpp rename to ton-test-liteclient-full/lite-client/crypto/parser/symtable.cpp index 6d76aff..38ffc16 100644 --- a/ton-test-liteclient-full/lite-client/crypto/func/symtable.hpp +++ b/ton-test-liteclient-full/lite-client/crypto/parser/symtable.cpp @@ -1,3 +1,7 @@ +#include "symtable.h" +#include +#include + namespace sym { /* @@ -6,45 +10,69 @@ namespace sym { * */ -typedef int var_idx_t; +int scope_level; -struct SymValBase { - enum { _Param, _Var, _Func, _Typename }; - int type; - int idx; - SymValBase(int _type, int _idx) : type(_type), idx(_idx) { - } - virtual ~SymValBase() = default; -}; +SymTable<100003> symbols; -/* - * - * SYMBOL TABLE - * - */ - -int scope_level; +SymDef* sym_def[symbols.hprime]; +SymDef* global_sym_def[symbols.hprime]; +std::vector> symbol_stack; +std::vector scope_opened_at; -struct SymDef { - int level; - src::sym_idx_t sym_idx; - SymValBase* value; - src::SrcLocation loc; - SymDef(int lvl, src::sym_idx_t idx, const src::SrcLocation& _loc = {}, SymValBase* val = 0) - : level(lvl), sym_idx(idx), value(val), loc(_loc) { +std::string Symbol::unknown_symbol_name(sym_idx_t i) { + if (!i) { + return "_"; + } else { + std::ostringstream os; + os << "SYM#" << i; + return os.str(); } - bool has_name() const { - return sym_idx; +} + +sym_idx_t SymTableBase::gen_lookup(std::string str, int mode, sym_idx_t idx) { + unsigned long long h1 = 1, h2 = 1; + for (char c : str) { + h1 = ((h1 * 239) + (unsigned char)(c)) % p; + h2 = ((h2 * 17) + (unsigned char)(c)) % (p - 1); } - std::string name() const { - return src::symbols.get_name(sym_idx); + ++h2; + ++h1; + while (true) { + if (sym_table[h1]) { + if (sym_table[h1]->str == str) { + return (mode & 2) ? not_found : sym_idx_t(h1); + } + h1 += h2; + if (h1 > p) { + h1 -= p; + } + } else { + if (!(mode & 1)) { + return not_found; + } + if (def_sym >= ((long)p * 3) / 4) { + throw SymTableOverflow{def_sym}; + } + sym_table[h1] = std::make_unique(str, idx <= 0 ? sym_idx_t(h1) : -idx); + ++def_sym; + return sym_idx_t(h1); + } } -}; +} -SymDef* sym_def[src::symbols.hprime]; -SymDef* global_sym_def[src::symbols.hprime]; -std::vector> symbol_stack; -std::vector scope_opened_at; +SymTableBase& SymTableBase::add_keyword(std::string str, sym_idx_t idx) { + if (idx <= 0) { + idx = ++def_kw; + } + sym_idx_t res = gen_lookup(str, -1, idx); + if (!res) { + throw SymTableKwRedef{str}; + } + if (idx < max_kw_idx) { + keywords[idx] = res; + } + return *this; +} void open_scope(src::Lexer& lex) { ++scope_level; @@ -80,7 +108,7 @@ void close_scope(src::Lexer& lex) { scope_opened_at.pop_back(); } -SymDef* lookup_symbol(src::sym_idx_t idx, int flags = 3) { +SymDef* lookup_symbol(sym_idx_t idx, int flags) { if (!idx) { return nullptr; } @@ -93,11 +121,11 @@ SymDef* lookup_symbol(src::sym_idx_t idx, int flags = 3) { return nullptr; } -SymDef* lookup_symbol(std::string name, int flags = 3) { - return lookup_symbol(src::symbols.lookup(name), flags); +SymDef* lookup_symbol(std::string name, int flags) { + return lookup_symbol(symbols.lookup(name), flags); } -SymDef* define_global_symbol(src::sym_idx_t name_idx, bool force_new = false, const src::SrcLocation& loc = {}) { +SymDef* define_global_symbol(sym_idx_t name_idx, bool force_new, const src::SrcLocation& loc) { if (!name_idx) { return nullptr; } @@ -108,7 +136,7 @@ SymDef* define_global_symbol(src::sym_idx_t name_idx, bool force_new = false, co return global_sym_def[name_idx] = new SymDef(0, name_idx, loc); } -SymDef* define_symbol(src::sym_idx_t name_idx, bool force_new = false, const src::SrcLocation& loc = {}) { +SymDef* define_symbol(sym_idx_t name_idx, bool force_new, const src::SrcLocation& loc) { if (!name_idx) { return nullptr; } diff --git a/ton-test-liteclient-full/lite-client/crypto/parser/symtable.h b/ton-test-liteclient-full/lite-client/crypto/parser/symtable.h new file mode 100644 index 0000000..b2a1b37 --- /dev/null +++ b/ton-test-liteclient-full/lite-client/crypto/parser/symtable.h @@ -0,0 +1,159 @@ +#pragma once +#include "srcread.h" +#include "lexer.h" +#include + +namespace sym { + +/* + * + * SYMBOL VALUES (DECLARED) + * + */ + +typedef int var_idx_t; + +struct SymValBase { + enum { _Param, _Var, _Func, _Typename }; + int type; + int idx; + SymValBase(int _type, int _idx) : type(_type), idx(_idx) { + } + virtual ~SymValBase() = default; +}; + +/* + * + * SYMBOL TABLE + * + */ + +// defined outside this module (by the end user) +int compute_symbol_subclass(std::string str); // return 0 if unneeded + +typedef int sym_idx_t; + +struct Symbol { + std::string str; + sym_idx_t idx; + int subclass; + Symbol(std::string _str, sym_idx_t _idx, int _sc) : str(_str), idx(_idx), subclass(_sc) { + } + Symbol(std::string _str, sym_idx_t _idx) : str(_str), idx(_idx) { + subclass = compute_symbol_subclass(std::move(_str)); + } + static std::string unknown_symbol_name(sym_idx_t i); +}; + +class SymTableBase { + unsigned p; + std::unique_ptr* sym_table; + sym_idx_t def_kw, def_sym; + constexpr static int max_kw_idx = 10000; + sym_idx_t keywords[max_kw_idx]; + + public: + SymTableBase(unsigned p_, std::unique_ptr* sym_table_) + : p(p_), sym_table(sym_table_), def_kw(0x100), def_sym(0) { + memset(keywords, 0, sizeof(keywords)); + } + constexpr static sym_idx_t not_found = 0; + SymTableBase& add_keyword(std::string str, sym_idx_t idx = 0); + SymTableBase& add_kw_char(char c) { + return add_keyword(std::string{c}, c); + } + sym_idx_t lookup(std::string str, int mode = 0) { + return gen_lookup(str, mode); + } + sym_idx_t lookup_add(std::string str) { + return gen_lookup(str, 1); + } + Symbol* operator[](sym_idx_t i) const { + return sym_table[i].get(); + } + bool is_keyword(sym_idx_t i) const { + return sym_table[i] && sym_table[i]->idx < 0; + } + std::string get_name(sym_idx_t i) const { + return sym_table[i] ? sym_table[i]->str : Symbol::unknown_symbol_name(i); + } + int get_subclass(sym_idx_t i) const { + return sym_table[i] ? sym_table[i]->subclass : 0; + } + Symbol* get_keyword(int i) const { + return ((unsigned)i < (unsigned)max_kw_idx) ? sym_table[keywords[i]].get() : nullptr; + } + + protected: + sym_idx_t gen_lookup(std::string str, int mode = 0, sym_idx_t idx = 0); +}; + +template +class SymTable : public SymTableBase { + public: + constexpr static int hprime = pp; + static int size() { + return pp + 1; + } + + private: + std::unique_ptr sym[pp + 1]; + + public: + SymTable() : SymTableBase(pp, sym) { + } + SymTable& add_keyword(std::string str, sym_idx_t idx = 0) { + SymTableBase::add_keyword(str, idx); + return *this; + } + SymTable& add_kw_char(char c) { + return add_keyword(std::string{c}, c); + } +}; + +struct SymTableOverflow { + int sym_def; + SymTableOverflow(int x) : sym_def(x) { + } +}; + +struct SymTableKwRedef { + std::string kw; + SymTableKwRedef(std::string _kw) : kw(_kw) { + } +}; + +extern SymTable<100003> symbols; + +extern int scope_level; + +struct SymDef { + int level; + sym_idx_t sym_idx; + SymValBase* value; + src::SrcLocation loc; + SymDef(int lvl, sym_idx_t idx, const src::SrcLocation& _loc = {}, SymValBase* val = 0) + : level(lvl), sym_idx(idx), value(val), loc(_loc) { + } + bool has_name() const { + return sym_idx; + } + std::string name() const { + return symbols.get_name(sym_idx); + } +}; + +extern SymDef* sym_def[symbols.hprime]; +extern SymDef* global_sym_def[symbols.hprime]; +extern std::vector> symbol_stack; +extern std::vector scope_opened_at; + +void open_scope(src::Lexer& lex); +void close_scope(src::Lexer& lex); +SymDef* lookup_symbol(sym_idx_t idx, int flags = 3); +SymDef* lookup_symbol(std::string name, int flags = 3); + +SymDef* define_global_symbol(sym_idx_t name_idx, bool force_new = false, const src::SrcLocation& loc = {}); +SymDef* define_symbol(sym_idx_t name_idx, bool force_new = false, const src::SrcLocation& loc = {}); + +} // namespace sym diff --git a/ton-test-liteclient-full/lite-client/crypto/test/test-db.cpp b/ton-test-liteclient-full/lite-client/crypto/test/test-db.cpp index 4998fe0..478192d 100644 --- a/ton-test-liteclient-full/lite-client/crypto/test/test-db.cpp +++ b/ton-test-liteclient-full/lite-client/crypto/test/test-db.cpp @@ -1817,6 +1817,19 @@ TEST(TonDb, StackOverflow) { LOG(ERROR) << "C"; } catch (...) { } + + struct A : public td::CntObject { + explicit A(td::Ref next) : next(next) { + } + td::Ref next; + }; + { + td::Ref head; + for (int i = 0; i < 10000000; i++) { + td::Ref new_head = td::Ref(true, std::move(head)); + head = std::move(new_head); + } + } } TEST(TonDb, BocRespectsUsageCell) { diff --git a/ton-test-liteclient-full/lite-client/crypto/tl/tlbc-data.h b/ton-test-liteclient-full/lite-client/crypto/tl/tlbc-data.h index 8cd57b4..9dcc32a 100644 --- a/ton-test-liteclient-full/lite-client/crypto/tl/tlbc-data.h +++ b/ton-test-liteclient-full/lite-client/crypto/tl/tlbc-data.h @@ -6,7 +6,7 @@ namespace tlbc { using src::Lexem; using src::Lexer; -using src::sym_idx_t; +using sym::sym_idx_t; struct Type; struct Constructor; @@ -79,7 +79,7 @@ struct TypeExpr { static TypeExpr* mk_mulint(const src::SrcLocation& loc, TypeExpr* expr1, TypeExpr* expr2); static TypeExpr* mk_cellref(const src::SrcLocation& loc, TypeExpr* expr1); static TypeExpr* mk_apply(const src::SrcLocation& loc, int tp, TypeExpr* expr1, TypeExpr* expr2); - static TypeExpr* mk_apply_empty(const src::SrcLocation& loc, src::sym_idx_t name, Type* type_applied); + static TypeExpr* mk_apply_empty(const src::SrcLocation& loc, sym_idx_t name, Type* type_applied); void show(std::ostream& os, const Constructor* cs = nullptr, int prio = 0, int mode = 0) const; private: diff --git a/ton-test-liteclient-full/lite-client/crypto/tl/tlbc-gen-cpp.cpp b/ton-test-liteclient-full/lite-client/crypto/tl/tlbc-gen-cpp.cpp index d96b562..7193226 100644 --- a/ton-test-liteclient-full/lite-client/crypto/tl/tlbc-gen-cpp.cpp +++ b/ton-test-liteclient-full/lite-client/crypto/tl/tlbc-gen-cpp.cpp @@ -392,7 +392,7 @@ void CppTypeCode::assign_class_name() { i = types.at(i).parent_type_idx; } if (name) { - type_name = src::symbols.get_name(name) + "_aux"; + type_name = sym::symbols.get_name(name) + "_aux"; } } cpp_type_class_name = global_cpp_ids.new_ident(type_name); @@ -408,7 +408,7 @@ void CppTypeCode::assign_cons_names() { for (int i = 0; i < cons_num; i++) { sym_idx_t cons = type.constructors.at(i)->constr_name; if (cons) { - cons_enum_name[i] = local_cpp_ids.new_ident(src::symbols.get_name(cons)); + cons_enum_name[i] = local_cpp_ids.new_ident(sym::symbols.get_name(cons)); } else if (type.const_param_idx >= 0) { int pv = type.constructors[i]->get_const_param(type.const_param_idx); cons_enum_name[i] = local_cpp_ids.new_ident(pv ? "cons" : "cons0", pv); @@ -3330,7 +3330,7 @@ void generate_cpp_output_to(std::ostream& os, int options = 0, std::vector=", _Geq); - symbols.add_keyword("!=", _Neq); - symbols.add_keyword("Type", _Type); - symbols.add_keyword("EMPTY", _EMPTY); + sym::symbols.add_kw_char('+') + .add_kw_char('-') + .add_kw_char('*') + .add_kw_char(':') + .add_kw_char(';') + .add_kw_char('(') + .add_kw_char(')') + .add_kw_char('{') + .add_kw_char('}') + .add_kw_char('[') + .add_kw_char(']') + .add_kw_char('=') + .add_kw_char('_') + .add_kw_char('?') + .add_kw_char('~') + .add_kw_char('^'); + + sym::symbols.add_keyword("==", _Eq) + .add_keyword("<=", _Leq) + .add_keyword(">=", _Geq) + .add_keyword("!=", _Neq) + .add_keyword("Type", _Type) + .add_keyword("EMPTY", _EMPTY); } // parses constant bitstrings in format \#[0-9a-f]*_? or \$[01]*_? @@ -129,6 +129,10 @@ int lexem_is_special(std::string str) { return get_special_value(str) ? Lexem::Special : 0; } +} // namespace src + +namespace sym { + enum class IdSc : char { undef = 0, lc = 1, uc = 2 }; // subclass: // 1 = first letter or first letter after last . is lowercase @@ -158,14 +162,14 @@ int compute_symbol_subclass(std::string str) { } inline bool is_lc_ident(sym_idx_t idx) { - return src::symbols.get_subclass(idx) == (int)IdSc::lc; + return symbols.get_subclass(idx) == (int)IdSc::lc; } inline bool is_uc_ident(sym_idx_t idx) { - return src::symbols.get_subclass(idx) == (int)IdSc::uc; + return symbols.get_subclass(idx) == (int)IdSc::uc; } -} // namespace src +} // namespace sym namespace tlbc { @@ -791,7 +795,7 @@ namespace tlbc { using src::Lexem; using src::Lexer; -using src::sym_idx_t; +using sym::sym_idx_t; /* * @@ -927,7 +931,7 @@ Field& Constructor::new_field(const src::SrcLocation& field_where, bool implicit } std::string Field::get_name() const { - return src::symbols.get_name(name); + return sym::symbols.get_name(name); } // register symbol in local symbol table @@ -958,7 +962,7 @@ bool TypeExpr::close(const src::SrcLocation& loc) { type->args.resize(type->arity, 0); } else if (type->arity != (int)args.size()) { throw src::ParseError{where, - std::string{"operator `"} + src::symbols.get_name(type->type_name) + + std::string{"operator `"} + sym::symbols.get_name(type->type_name) + "` applied with incorrect number of arguments, partial type applications not supported"}; return false; } @@ -974,7 +978,7 @@ bool TypeExpr::close(const src::SrcLocation& loc) { if (!is_eq) { if (x & Type::_IsPos) { throw src::ParseError{arg->where, std::string{"passed an argument of incorrect polarity to `"} + - src::symbols.get_name(type->type_name) + "`"}; + sym::symbols.get_name(type->type_name) + "`"}; } x |= Type::_IsNeg; } else if (neg_cnt == 2) { @@ -1004,7 +1008,7 @@ TypeExpr* TypeExpr::mk_apply_empty(const src::SrcLocation& loc, sym_idx_t name, void Type::print_name(std::ostream& os) const { if (type_name) { - os << src::symbols.get_name(type_name); + os << sym::symbols.get_name(type_name); } else { os << "TYPE_" << type_idx; } @@ -1012,7 +1016,7 @@ void Type::print_name(std::ostream& os) const { std::string Type::get_name() const { if (type_name) { - return src::symbols.get_name(type_name); + return sym::symbols.get_name(type_name); } else { std::ostringstream os; os << "TYPE_" << type_idx; @@ -1021,7 +1025,7 @@ std::string Type::get_name() const { } std::string Constructor::get_name() const { - return src::symbols.get_name(constr_name); + return sym::symbols.get_name(constr_name); } std::string Constructor::get_qualified_name() const { @@ -1046,7 +1050,7 @@ void TypeExpr::show(std::ostream& os, const Constructor* cs, int prio, int mode) os << '~'; } if (param_name > 0) { - os << src::symbols.get_name(param_name); + os << sym::symbols.get_name(param_name); } else { os << '_' << i + 1; } @@ -1616,8 +1620,8 @@ void parse_implicit_param(Lexer& lex, Constructor& cs) { sym::SymDef* register_new_type(const src::SrcLocation& loc, sym_idx_t name) { // unknown identifier, declare new type - if (!src::is_uc_ident(name)) { - throw src::ParseError{loc, std::string{"implicitly defined type `"} + src::symbols.get_name(name) + + if (!sym::is_uc_ident(name)) { + throw src::ParseError{loc, std::string{"implicitly defined type `"} + sym::symbols.get_name(name) + "` must begin with an uppercase letter"}; } sym::SymDef* sym_def = sym::define_global_symbol(name, true, loc); @@ -1659,7 +1663,7 @@ void Constructor::show(std::ostream& os, int mode) const { if (mode & 4) { os << '['; } else { - os << src::symbols.get_name(constr_name); + os << sym::symbols.get_name(constr_name); } if (!(mode & 8)) { show_tag(os, tag); @@ -1692,7 +1696,7 @@ void Constructor::show(std::ostream& os, int mode) const { if (type_defined) { type_defined->print_name(os); } else { - os << src::symbols.get_name(type_name); + os << sym::symbols.get_name(type_name); } for (int i = 0; i < type_arity; i++) { os << ' '; @@ -1732,14 +1736,14 @@ void Constructor::check_assign_tag() { set_tag(computed_tag); if (show_tag_warnings) { std::ostringstream os; - os << "constructor `" << src::symbols.get_name(type_name) << "::" << src::symbols.get_name(constr_name) + os << "constructor `" << sym::symbols.get_name(type_name) << "::" << sym::symbols.get_name(constr_name) << "` had no tag, assigned "; show_tag(os, computed_tag); where.show_warning(os.str()); } } else if (tag != computed_tag && show_tag_warnings) { std::ostringstream os; - os << "constructor `" << src::symbols.get_name(type_name) << "::" << src::symbols.get_name(constr_name) + os << "constructor `" << sym::symbols.get_name(type_name) << "::" << sym::symbols.get_name(constr_name) << "` has explicit tag "; show_tag(os, tag); os << " different from its computed tag "; @@ -1753,8 +1757,8 @@ void Constructor::check_assign_tag() { void Type::bind_constructor(const src::SrcLocation& loc, Constructor* cs) { if (is_final) { - throw src::ParseError{loc, std::string{"cannot add new constructor `"} + src::symbols.get_name(cs->constr_name) + - "` to a finalized type `" + src::symbols.get_name(type_name) + "`"}; + throw src::ParseError{loc, std::string{"cannot add new constructor `"} + sym::symbols.get_name(cs->constr_name) + + "` to a finalized type `" + sym::symbols.get_name(type_name) + "`"}; } if (arity < 0) { arity = cs->type_arity; @@ -1762,7 +1766,7 @@ void Type::bind_constructor(const src::SrcLocation& loc, Constructor* cs) { args.resize(arity, 0); } else { if (arity != cs->type_arity) { - throw src::ParseError{loc, std::string{"parametrized type `"} + src::symbols.get_name(type_name) + + throw src::ParseError{loc, std::string{"parametrized type `"} + sym::symbols.get_name(type_name) + "` redefined with different arity"}; } } @@ -1774,12 +1778,12 @@ void Type::bind_constructor(const src::SrcLocation& loc, Constructor* cs) { int& x = args[i]; x |= (expr->is_nat ? _IsNat : _IsType); if ((x & (_IsNat | _IsType)) == (_IsNat | _IsType)) { - throw src::ParseError{expr->where, std::string{"formal parameter to type `"} + src::symbols.get_name(type_name) + + throw src::ParseError{expr->where, std::string{"formal parameter to type `"} + sym::symbols.get_name(type_name) + "` has incorrect type"}; } x |= (negated ? _IsNeg : _IsPos); if ((x & (_IsPos | _IsNeg)) == (_IsPos | _IsNeg)) { - throw src::ParseError{expr->where, std::string{"formal parameter to type `"} + src::symbols.get_name(type_name) + + throw src::ParseError{expr->where, std::string{"formal parameter to type `"} + sym::symbols.get_name(type_name) + "` has incorrect polarity"}; } if (cs->param_const_val.at(i) < 0) { @@ -1819,8 +1823,8 @@ void Type::bind_constructor(const src::SrcLocation& loc, Constructor* cs) { if (cs->constr_name) { for (auto c : constructors) { if (c->constr_name == cs->constr_name) { - std::string tname = src::symbols.get_name(type_name); - std::string cname = tname + "::" + src::symbols.get_name(cs->constr_name); + std::string tname = sym::symbols.get_name(type_name); + std::string cname = tname + "::" + sym::symbols.get_name(cs->constr_name); c->where.show_note(std::string{"constructor `"} + cname + "` first defined here"); throw src::ParseError{cs->where, std::string{"constructor `"} + cname + "` redefined"}; } @@ -1958,7 +1962,7 @@ TypeExpr* parse_term(Lexer& lex, Constructor& cs, int mode) { } sym_def = register_new_type(lex.cur().loc, name); if (verbosity > 2) { - std::cerr << "implicitly defined new type `" << src::symbols.get_name(name) << "`" << std::endl; + std::cerr << "implicitly defined new type `" << sym::symbols.get_name(name) << "`" << std::endl; } } if (!sym_def->value) { @@ -2105,7 +2109,7 @@ TypeExpr* parse_expr10(Lexer& lex, Constructor& cs, int mode) { return expr; } // std::cerr << "parse <=>, mode " << mode << std::endl; - src::sym_idx_t op_name = lex.cur().val; + sym_idx_t op_name = lex.cur().val; src::SrcLocation where = lex.cur().loc; expr->close(where); if (!(mode & 1)) { @@ -2199,7 +2203,7 @@ void parse_field_list(Lexer& lex, Constructor& cs) { } void parse_constructor_def(Lexer& lex) { - if (lex.tp() != '_' && (lex.tp() != src::_Ident || !src::is_lc_ident(lex.cur().val))) { + if (lex.tp() != '_' && (lex.tp() != src::_Ident || !sym::is_lc_ident(lex.cur().val))) { throw src::ParseError{lex.cur().loc, "constructor name lowercase identifier expected"}; } sym::open_scope(lex); @@ -2214,13 +2218,13 @@ void parse_constructor_def(Lexer& lex) { assert(tag); lex.next(); } - //std::cerr << "parsing constructor `" << src::symbols.get_name(constr_name) << "` with tag " << std::hex << tag + //std::cerr << "parsing constructor `" << sym::symbols.get_name(constr_name) << "` with tag " << std::hex << tag // << std::dec << std::endl; auto cs_ref = new Constructor(where, constr_name, 0, tag); Constructor& cs = *cs_ref; parse_field_list(lex, cs); lex.expect('='); - if (lex.tp() != src::_Ident || !src::is_uc_ident(lex.cur().val)) { + if (lex.tp() != src::_Ident || !sym::is_uc_ident(lex.cur().val)) { throw src::ParseError{lex.cur().loc, "type name uppercase identifier expected"}; } Lexem type_lex = lex.cur(); @@ -2229,7 +2233,7 @@ void parse_constructor_def(Lexer& lex) { if (!sym_def) { sym_def = register_new_type(lex.cur().loc, type_name); if (verbosity > 2) { - std::cerr << "defined new type `" << src::symbols.get_name(type_name) << "`" << std::endl; + std::cerr << "defined new type `" << sym::symbols.get_name(type_name) << "`" << std::endl; } assert(sym_def); } @@ -2321,7 +2325,7 @@ bool parse_source_stdin() { Type* define_builtin_type(std::string name_str, std::string args, bool produces_nat, int size = -1, int min_size = -1, bool any_bits = false, int is_int = 0) { - sym_idx_t name = src::symbols.lookup_add(name_str); + sym_idx_t name = sym::symbols.lookup_add(name_str); assert(name_str.size() && name); int arity = (int)args.size(); types.emplace_back(types_num++, name, produces_nat, arity, true, true); @@ -2350,7 +2354,7 @@ Type* define_builtin_type(std::string name_str, std::string args, bool produces_ } Type* lookup_type(std::string name_str) { - sym_idx_t name = src::symbols.lookup(name_str); + sym_idx_t name = sym::symbols.lookup(name_str); if (name) { auto sym_def = sym::lookup_symbol(name); if (sym_def) { @@ -2390,10 +2394,10 @@ void define_builtins() { Eq_type = define_builtin_type("=", "##", false, 0, 0, true); Less_type = define_builtin_type("<", "##", false, 0, 0, true); Leq_type = define_builtin_type("<=", "##", false, 0, 0, true); - Nat_name = src::symbols.lookup("#"); - Eq_name = src::symbols.lookup("="); - Less_name = src::symbols.lookup("<"); - Leq_name = src::symbols.lookup("<="); + Nat_name = sym::symbols.lookup("#"); + Eq_name = sym::symbols.lookup("="); + Less_name = sym::symbols.lookup("<"); + Leq_name = sym::symbols.lookup("<="); builtin_types_num = types_num; } @@ -2895,7 +2899,7 @@ void dump_all_types() { assert(sym_def); throw src::ParseError{ sym_def ? sym_def->loc : src::SrcLocation{}, - std::string{"implicitly defined type `"} + src::symbols.get_name(type.type_name) + "` has no constructors"}; + std::string{"implicitly defined type `"} + sym::symbols.get_name(type.type_name) + "` has no constructors"}; } } } diff --git a/ton-test-liteclient-full/lite-client/crypto/tl/tlblib.hpp b/ton-test-liteclient-full/lite-client/crypto/tl/tlblib.hpp index 4e2e890..c30043d 100644 --- a/ton-test-liteclient-full/lite-client/crypto/tl/tlblib.hpp +++ b/ton-test-liteclient-full/lite-client/crypto/tl/tlblib.hpp @@ -21,6 +21,12 @@ class TLB { virtual bool validate(const vm::CellSlice& cs) const { return cs.have_ext(get_size(cs)); } + virtual bool validate_exact(const vm::CellSlice& cs) const { + return (int)cs.size_ext() == get_size(cs); + } + bool validate_csr(Ref cs_ref) const { + return cs_ref.not_null() && validate_skip_exact(cs_ref.write()); + } Ref fetch(vm::CellSlice& cs) const { return cs.fetch_subslice_ext(get_size(cs)); } @@ -62,10 +68,17 @@ class TLB { virtual bool validate_skip(vm::CellSlice& cs) const { return validate(cs) && skip(cs); } + bool validate_skip_exact(vm::CellSlice& cs) const { + return validate_skip(cs) && cs.empty_ext(); + } bool validate_by_skip(const vm::CellSlice& cs) const { vm::CellSlice copy{cs}; return validate_skip(copy); } + bool validate_by_skip_exact(const vm::CellSlice& cs) const { + vm::CellSlice copy{cs}; + return validate_skip_exact(copy); + } bool extract_by_skip(vm::CellSlice& cs) const { vm::CellSlice copy{cs}; return skip(copy) && cs.cut_tail(copy); @@ -115,6 +128,12 @@ class TLB { bool as_integer_skip_to(vm::CellSlice& cs, td::RefInt256& res) const { return (res = as_integer_skip(cs)).not_null(); } + bool as_integer_to(const vm::CellSlice& cs, td::RefInt256& res) const { + return (res = as_integer(cs)).not_null(); + } + bool as_integer_to(Ref cs_ref, td::RefInt256& res) const { + return (res = as_integer(std::move(cs_ref))).not_null(); + } bool validate_ref(Ref cell_ref) const { return cell_ref.not_null() && validate_ref_internal(std::move(cell_ref)); } @@ -191,6 +210,9 @@ struct TLB_Complex : TLB { bool validate(const vm::CellSlice& cs) const override { return validate_by_skip(cs); } + bool validate_exact(const vm::CellSlice& cs) const override { + return validate_by_skip_exact(cs); + } bool extract(vm::CellSlice& cs) const override { return extract_by_skip(cs); } @@ -205,10 +227,12 @@ struct TLB_Complex : TLB { } td::RefInt256 as_integer(const vm::CellSlice& cs) const override { vm::CellSlice copy{cs}; - return as_integer_skip(copy); + auto res = as_integer_skip(copy); + return res.not_null() && copy.empty_ext() ? std::move(res) : td::RefInt256{}; } td::RefInt256 as_integer(Ref cs) const override { - return as_integer_skip(cs.write()); + auto res = as_integer_skip(cs.write()); + return res.not_null() && cs->empty_ext() ? std::move(res) : td::RefInt256{}; } }; diff --git a/ton-test-liteclient-full/lite-client/crypto/vm/boc.cpp b/ton-test-liteclient-full/lite-client/crypto/vm/boc.cpp index ef54691..b19c5e4 100644 --- a/ton-test-liteclient-full/lite-client/crypto/vm/boc.cpp +++ b/ton-test-liteclient-full/lite-client/crypto/vm/boc.cpp @@ -76,12 +76,12 @@ td::Result> CellSerializationInfo::create_data_cell(td::Slice cell for (int k = 0; k < refs_cnt; k++) { cb.store_ref(std::move(refs[k])); } - auto res = cb.finalize(special); + auto res = cb.finalize_novm(special); if (res.is_null()) { return td::Status::Error("CellBuilder::finalize failed"); } if (res->is_special() != special) { - return td::Status::Error("is_special missmatch"); + return td::Status::Error("is_special mismatch"); } if (res->get_level_mask() != level_mask) { return td::Status::Error("level mask mismatch"); diff --git a/ton-test-liteclient-full/lite-client/crypto/vm/cells/CellBuilder.cpp b/ton-test-liteclient-full/lite-client/crypto/vm/cells/CellBuilder.cpp index 1d672e3..b8680f0 100644 --- a/ton-test-liteclient-full/lite-client/crypto/vm/cells/CellBuilder.cpp +++ b/ton-test-liteclient-full/lite-client/crypto/vm/cells/CellBuilder.cpp @@ -41,11 +41,7 @@ Ref CellBuilder::finalize_copy(bool special) const { return res.move_as_ok(); } -Ref CellBuilder::finalize(bool special) { - auto* vm_state_interface = VmStateInterface::get(); - if (vm_state_interface) { - vm_state_interface->register_cell_create(); - } +Ref CellBuilder::finalize_novm(bool special) { auto res = DataCell::create(data, size(), td::mutable_span(refs.data(), size_refs()), special); bits = refs_cnt = 0; if (res.is_error()) { @@ -56,6 +52,14 @@ Ref CellBuilder::finalize(bool special) { return res.move_as_ok(); } +Ref CellBuilder::finalize(bool special) { + auto* vm_state_interface = VmStateInterface::get(); + if (vm_state_interface) { + vm_state_interface->register_cell_create(); + } + return finalize_novm(special); +} + Ref CellBuilder::create_pruned_branch(Ref cell, td::uint32 new_level, td::uint32 virt_level) { if (cell->is_loaded() && cell->get_level() <= virt_level && cell->get_virtualization() == 0) { CellSlice cs(NoVm{}, cell); @@ -518,11 +522,11 @@ CellBuilder* CellBuilder::make_copy() const { return c; } -CellSlice CellBuilder::as_cellslice() const& { +CellSlice CellBuilder::as_cellslice() const & { return CellSlice{finalize_copy()}; } -Ref CellBuilder::as_cellslice_ref() const& { +Ref CellBuilder::as_cellslice_ref() const & { return Ref{true, finalize_copy()}; } diff --git a/ton-test-liteclient-full/lite-client/crypto/vm/cells/CellBuilder.h b/ton-test-liteclient-full/lite-client/crypto/vm/cells/CellBuilder.h index 5484ef0..d406e71 100644 --- a/ton-test-liteclient-full/lite-client/crypto/vm/cells/CellBuilder.h +++ b/ton-test-liteclient-full/lite-client/crypto/vm/cells/CellBuilder.h @@ -153,12 +153,13 @@ class CellBuilder : public td::CntObject { bool can_extend_by(std::size_t bits, unsigned refs) const; Ref finalize_copy(bool special = false) const; Ref finalize(bool special = false); + Ref finalize_novm(bool special = false); bool finalize_to(Ref& res, bool special = false) { return (res = finalize(special)).not_null(); } - CellSlice as_cellslice() const&; + CellSlice as_cellslice() const &; CellSlice as_cellslice() &&; - Ref as_cellslice_ref() const&; + Ref as_cellslice_ref() const &; Ref as_cellslice_ref() &&; static td::int64 get_total_cell_builders() { return get_thread_safe_counter().sum(); diff --git a/ton-test-liteclient-full/lite-client/crypto/vm/cells/CellHash.h b/ton-test-liteclient-full/lite-client/crypto/vm/cells/CellHash.h index 8590f6e..25d4ce6 100644 --- a/ton-test-liteclient-full/lite-client/crypto/vm/cells/CellHash.h +++ b/ton-test-liteclient-full/lite-client/crypto/vm/cells/CellHash.h @@ -69,6 +69,6 @@ struct hash { namespace vm { template H AbslHashValue(H h, const CellHash& cell_hash) { - return AbslHashValue(std::move(h), std::hash()(cell_hash)); + return H::combine(std::move(h), std::hash()(cell_hash)); } } // namespace vm diff --git a/ton-test-liteclient-full/lite-client/crypto/vm/cells/CellSlice.h b/ton-test-liteclient-full/lite-client/crypto/vm/cells/CellSlice.h index 8357270..93c08e7 100644 --- a/ton-test-liteclient-full/lite-client/crypto/vm/cells/CellSlice.h +++ b/ton-test-liteclient-full/lite-client/crypto/vm/cells/CellSlice.h @@ -24,8 +24,8 @@ class CellSlice : public td::CntObject { mutable unsigned zd; public: - const long long fetch_long_eof = (static_cast(-1LL) << 63); - const unsigned long long fetch_ulong_eof = (unsigned long long)-1LL; + static constexpr long long fetch_long_eof = (static_cast(-1LL) << 63); + static constexpr unsigned long long fetch_ulong_eof = (unsigned long long)-1LL; struct CellReadError {}; CellSlice(NoVm, Ref cell_ref); diff --git a/ton-test-liteclient-full/lite-client/crypto/vm/continuation.h b/ton-test-liteclient-full/lite-client/crypto/vm/continuation.h index f795fdf..0203d6f 100644 --- a/ton-test-liteclient-full/lite-client/crypto/vm/continuation.h +++ b/ton-test-liteclient-full/lite-client/crypto/vm/continuation.h @@ -333,9 +333,11 @@ struct GasLimits { } void change_limit(long long _limit); void consume(long long amount) { + // LOG(DEBUG) << "consume " << amount << " gas (" << gas_remaining << " remaining)"; gas_remaining -= amount; } bool try_consume(long long amount) { + // LOG(DEBUG) << "try consume " << amount << " gas (" << gas_remaining << " remaining)"; return (gas_remaining -= amount) >= 0; } void gas_exception() const; diff --git a/ton-test-liteclient-full/lite-client/crypto/vm/dict.cpp b/ton-test-liteclient-full/lite-client/crypto/vm/dict.cpp index 6bc9ff1..e27bb3b 100644 --- a/ton-test-liteclient-full/lite-client/crypto/vm/dict.cpp +++ b/ton-test-liteclient-full/lite-client/crypto/vm/dict.cpp @@ -208,6 +208,15 @@ Ref DictionaryFixed::finish_create_fork(CellBuilder& cb, Ref c1, Ref return cb.finalize(); } +bool DictionaryFixed::check_fork_raw(Ref cs_ref, int n) const { + if (cs_ref.is_null()) { + return false; + } + Ref c1, c2; + CellSlice& cs = cs_ref.write(); + return cs.fetch_ref_to(c1) && cs.fetch_ref_to(c2) && check_fork(cs, std::move(c1), std::move(c2), n); +} + /* * * Label parser (HmLabel n ~l) for all dictionary types @@ -1725,6 +1734,7 @@ static inline bool set_bit(td::BitPtr ptr, bool value = true) { return true; } +// mode: +1 = check augmentation of dict1, +2 = ... of dict2 bool DictionaryFixed::dict_scan_diff(Ref dict1, Ref dict2, td::BitPtr key_buffer, int n, int total_key_len, const scan_diff_func_t& diff_func, int mode, int skip1, int skip2) const { // skip1: remove that much first bits from all keys in dictionary dict1 (its keys are actually n + skip1 bits long) @@ -1741,10 +1751,17 @@ bool DictionaryFixed::dict_scan_diff(Ref dict1, Ref dict2, td::BitPt if (label.l_bits >= n) { assert(label.l_bits == n); // leaf in dict2, empty dict1 - return diff_func(key_buffer + label.l_bits - total_key_len, total_key_len, {}, std::move(label.remainder)); + auto key = key_buffer + label.l_bits - total_key_len; + if ((mode & 2) && !check_leaf(label.remainder, key, total_key_len)) { + throw VmError{Excno::dict_err, "invalid leaf in the second dictionary being compared"}; + } + return diff_func(key, total_key_len, {}, std::move(label.remainder)); } n -= label.l_bits + 1; key_buffer += label.l_bits + 1; + if ((mode & 2) && !check_fork_raw(label.remainder, n + 1)) { + throw VmError{Excno::dict_err, "invalid fork in the second dictionary being compared"}; + } // compare {} with each of children of dict2 for (unsigned sw = 0; sw < 2; sw++) { key_buffer[-1] = (bool)sw; @@ -1761,10 +1778,17 @@ bool DictionaryFixed::dict_scan_diff(Ref dict1, Ref dict2, td::BitPt if (label.l_bits >= n) { assert(label.l_bits == n); // leaf in dict1, empty dict2 - return diff_func(key_buffer + label.l_bits - total_key_len, total_key_len, std::move(label.remainder), {}); + auto key = key_buffer + label.l_bits - total_key_len; + if ((mode & 1) && !check_leaf(label.remainder, key, total_key_len)) { + throw VmError{Excno::dict_err, "invalid leaf in the first dictionary being compared"}; + } + return diff_func(key, total_key_len, std::move(label.remainder), {}); } n -= label.l_bits + 1; key_buffer += label.l_bits + 1; + if ((mode & 1) && !check_fork_raw(label.remainder, n + 1)) { + throw VmError{Excno::dict_err, "invalid fork in the first dictionary being compared"}; + } // compare each of children of dict1 with {} for (unsigned sw = 0; sw < 2; sw++) { key_buffer[-1] = (bool)sw; @@ -1806,13 +1830,25 @@ bool DictionaryFixed::dict_scan_diff(Ref dict1, Ref dict2, td::BitPt label2.skip_label(); if (c == n) { // our two dictionaries are in fact leafs with matching edge labels (keys) + auto key = key_buffer + n - total_key_len; + if ((mode & 1) && !check_leaf(label1.remainder, key, total_key_len)) { + throw VmError{Excno::dict_err, "invalid leaf in the first dictionary being compared"}; + } + if ((mode & 2) && !check_leaf(label2.remainder, key, total_key_len)) { + throw VmError{Excno::dict_err, "invalid leaf in the second dictionary being compared"}; + } return label1.remainder->contents_equal(*label2.remainder) || - diff_func(key_buffer + n - total_key_len, total_key_len, std::move(label1.remainder), - std::move(label2.remainder)); + diff_func(key, total_key_len, std::move(label1.remainder), std::move(label2.remainder)); } assert(c < n); key_buffer += c + 1; n -= c + 1; + if ((mode & 1) && !check_fork_raw(label1.remainder, n + 1)) { + throw VmError{Excno::dict_err, "invalid fork in the first dictionary being compared"}; + } + if ((mode & 2) && !check_fork_raw(label2.remainder, n + 1)) { + throw VmError{Excno::dict_err, "invalid fork in the second dictionary being compared"}; + } for (unsigned sw = 0; sw <= 1; sw++) { key_buffer[-1] = (bool)sw; // compare left and then right subtrees @@ -1826,6 +1862,9 @@ bool DictionaryFixed::dict_scan_diff(Ref dict1, Ref dict2, td::BitPt if (c == l1) { assert(c < l2); dict1.clear(); + if ((mode & 1) && !check_fork_raw(label1.remainder, n - c)) { + throw VmError{Excno::dict_err, "invalid fork in the first dictionary being compared"}; + } // children of root node of dict1 auto c1 = label1.remainder->prefetch_ref(0); auto c2 = label1.remainder->prefetch_ref(1); @@ -1852,8 +1891,11 @@ bool DictionaryFixed::dict_scan_diff(Ref dict1, Ref dict2, td::BitPt } else { assert(c == l2 && c < l1); dict2.clear(); - // children of root node of dict2 label2.skip_label(); // dict2 had shorter label anyway, label1 is already unpacked + if ((mode & 2) && !check_fork_raw(label2.remainder, n - c)) { + throw VmError{Excno::dict_err, "invalid fork in the second dictionary being compared"}; + } + // children of root node of dict2 auto c1 = label2.remainder->prefetch_ref(0); auto c2 = label2.remainder->prefetch_ref(1); label2.remainder.clear(); @@ -1894,6 +1936,68 @@ bool DictionaryFixed::scan_diff(DictionaryFixed& dict2, const scan_diff_func_t& } } +bool DictionaryFixed::dict_validate_check(Ref dict, td::BitPtr key_buffer, int n, int total_key_len, + const DictionaryFixed::foreach_func_t& foreach_func, + bool invert_first) const { + //LOG(DEBUG) << "dict_validate_check for " << total_key_len - n << "-bit key prefix " << (key_buffer - n + total_key_len).to_hex(total_key_len - n); + if (dict.is_null()) { + return true; + } + LabelParser label{std::move(dict), n, label_mode()}; + int l = label.l_bits; + label.extract_label_to(key_buffer); + if (l == n) { + // leaf node, value left in label.remainder + vm::CellSlice cs{*label.remainder}; + auto key = key_buffer + n - total_key_len; + if (!(check_leaf(cs, key, total_key_len) && foreach_func(std::move(label.remainder), key, total_key_len))) { + LOG(DEBUG) << "invalid dictionary leaf node with " << total_key_len << "-bit key " << key.to_hex(total_key_len); + return false; + } + return true; + } + assert(l >= 0 && l < n); + // a fork with two children, c1 and c2 + auto c1 = label.remainder.write().fetch_ref(); + auto c2 = label.remainder.unique_write().fetch_ref(); + key_buffer += l + 1; + n -= l + 1; + if (!check_fork(label.remainder.write(), c1, c2, n + 1)) { + LOG(DEBUG) << "invalid dictionary fork augmentation for fork node with " << total_key_len - n - 1 + << "-bit key prefix " << (key_buffer + n - total_key_len).to_hex(total_key_len - n - 1); + return false; + } + label.remainder.clear(); + if (l) { + invert_first = false; + } else if (invert_first) { + std::swap(c1, c2); + } + key_buffer[-1] = invert_first; + // recursive check_foreach applied to both children + if (!dict_validate_check(std::move(c1), key_buffer, n, total_key_len, foreach_func)) { + return false; + } + key_buffer[-1] = !invert_first; + return dict_validate_check(std::move(c2), key_buffer, n, total_key_len, foreach_func); +} + +bool DictionaryFixed::validate_check(const DictionaryFixed::foreach_func_t& foreach_func, bool invert_first) { + if (!validate()) { + return false; + } + if (is_empty()) { + return true; + } + int key_len = get_key_bits(); + unsigned char key_buffer[max_key_bytes]; + return dict_validate_check(get_root_cell(), td::BitPtr{key_buffer}, key_len, key_len, foreach_func, invert_first); +} + +bool DictionaryFixed::validate_all() { + return validate_check([](Ref value, td::ConstBitPtr key, int n) { return true; }) || invalidate(); +} + /* * * PREFIX DICTIONARIES @@ -2020,6 +2124,11 @@ Ref AugmentationData::extract_extra(Ref cs_ref) co return skip_extra(cs) && cs_ref.write().cut_tail(cs) ? std::move(cs_ref) : Ref{}; } +bool AugmentationData::extract_extra_to(vm::CellSlice& cs, vm::CellSlice& extra) const { + extra = cs; + return cs.is_valid() && skip_extra(cs) && extra.cut_tail(cs); +} + } // namespace dict using dict::AugmentationData; @@ -2075,8 +2184,20 @@ bool AugmentedDictionary::validate() { if (root_cell.not_null()) { return invalidate(); } + vm::CellSlice cs{*root}; + if (!cs.advance(1)) { + return invalidate(); + } if (non_empty) { - root_cell = root->prefetch_ref(); + root_cell = cs.fetch_ref(); + auto root_extra = get_root_extra(); + if (!(root_extra.not_null() && root_extra->contents_equal(cs))) { + return invalidate(); + } + } else { + if (!aug.check_empty(cs)) { + return invalidate(); + } } } else if (root.not_null()) { return invalidate(); @@ -2158,11 +2279,15 @@ Ref AugmentedDictionary::get_node_extra(Ref cell_ref, int n) co } LabelParser label{std::move(cell_ref), n, 2}; label.skip_label(); - if (label.l_bits == n || label.remainder.write().advance_refs(2)) { + if (label.l_bits == n) { return aug.extract_extra(std::move(label.remainder)); - } else { - return {}; + } else if (label.remainder.write().advance_refs(2)) { + vm::CellSlice cs{*label.remainder}; + if (aug.skip_extra(cs) && cs.empty_ext()) { + return std::move(label.remainder); + } } + return {}; } Ref AugmentedDictionary::get_root_extra() const { @@ -2245,6 +2370,20 @@ std::pair, Ref> AugmentedDictionary::lookup_delete_ext return decompose_value_extra(lookup_delete_with_extra(key, key_len)); } +bool AugmentedDictionary::check_leaf(CellSlice& cs, td::ConstBitPtr key, int key_len) const { + vm::CellSlice extra; + return aug.extract_extra_to(cs, extra) && aug.check_leaf_key_extra(cs, extra, key, key_len); +} + +bool AugmentedDictionary::check_fork(CellSlice& cs, Ref c1, Ref c2, int n) const { + if (n <= 0) { + return false; + } + auto extra1 = get_node_extra(std::move(c1), n - 1); + auto extra2 = get_node_extra(std::move(c2), n - 1); + return extra1.not_null() && extra2.not_null() && aug.check_fork(cs, extra1.write(), extra2.write()); +} + Ref AugmentedDictionary::finish_create_leaf(CellBuilder& cb, const CellSlice& value) const { CellSlice value_copy{value}; if (!aug.eval_leaf(cb, value_copy)) { @@ -2470,4 +2609,16 @@ std::pair, Ref> AugmentedDictionary::traverse_extra(td return dict_traverse_extra(get_root_cell(), key_buffer, key_len, traverse_node); } +bool AugmentedDictionary::validate_check_extra(const AugmentedDictionary::foreach_extra_func_t& foreach_extra_func, + bool invert_first) { + const AugmentationData& augm = aug; + int key_len = get_key_bits(); + return validate_check( + [&foreach_extra_func, &augm, key_len](Ref value_extra, td::ConstBitPtr key, int value) { + auto extra = augm.extract_extra(value_extra.write()); + return extra.not_null() && foreach_extra_func(std::move(value_extra), std::move(extra), key, key_len); + }, + invert_first); +} + } // namespace vm diff --git a/ton-test-liteclient-full/lite-client/crypto/vm/dict.h b/ton-test-liteclient-full/lite-client/crypto/vm/dict.h index 6c08609..b84824e 100644 --- a/ton-test-liteclient-full/lite-client/crypto/vm/dict.h +++ b/ton-test-liteclient-full/lite-client/crypto/vm/dict.h @@ -57,8 +57,19 @@ struct AugmentationData { virtual bool check_leaf(vm::CellSlice& cs, vm::CellSlice& val_cs) const; virtual bool check_fork(vm::CellSlice& cs, vm::CellSlice& left_cs, vm::CellSlice& right_cs) const; virtual bool check_empty(vm::CellSlice& cs) const; + virtual bool check_leaf_key_extra(vm::CellSlice& val_cs, vm::CellSlice& extra_cs, td::ConstBitPtr key, + int key_len) const { + return check_leaf(extra_cs, val_cs); + } Ref extract_extra(vm::CellSlice& cs) const; Ref extract_extra(Ref cs_ref) const; + bool extract_extra_to(vm::CellSlice& cs, Ref& extra_csr) const { + return (extra_csr = extract_extra(cs)).not_null(); + } + bool extract_extra_to(Ref cs_ref, Ref& extra_csr) const { + return (extra_csr = extract_extra(std::move(cs_ref))).not_null(); + } + bool extract_extra_to(vm::CellSlice& cs, vm::CellSlice& extra) const; }; static inline bool store_cell_dict(vm::CellBuilder& cb, Ref dict_root) { @@ -190,6 +201,8 @@ class DictionaryFixed : public DictionaryBase { bool combine_with(DictionaryFixed& dict2, const simple_combine_func_t& simple_combine_func, int mode = 0); bool combine_with(DictionaryFixed& dict2); bool scan_diff(DictionaryFixed& dict2, const scan_diff_func_t& diff_func, int check_augm = 0); + bool validate_check(const foreach_func_t& foreach_func, bool invert_first = false); + bool validate_all(); template bool key_exists(const T& key) { return key_exists(key.bits(), key.size()); @@ -198,6 +211,10 @@ class DictionaryFixed : public DictionaryBase { Ref lookup(const T& key) { return lookup(key.bits(), key.size()); } + template + Ref get_minmax_key(T& key_buffer, bool fetch_max = false, bool invert_first = false) { + return get_minmax_key(key_buffer.bits(), key_buffer.size(), fetch_max, invert_first); + } protected: virtual int label_mode() const { @@ -205,6 +222,16 @@ class DictionaryFixed : public DictionaryBase { } virtual Ref finish_create_leaf(CellBuilder& cb, const CellSlice& value) const; virtual Ref finish_create_fork(CellBuilder& cb, Ref c1, Ref c2, int n) const; + virtual bool check_fork(CellSlice& cs, Ref c1, Ref c2, int n) const { + return true; + } + virtual bool check_leaf(CellSlice& cs, td::ConstBitPtr key, int key_len) const { + return true; + } + bool check_leaf(Ref cs_ref, td::ConstBitPtr key, int key_len) const { + return check_leaf(cs_ref.write(), key, key_len); + } + bool check_fork_raw(Ref cs_ref, int n) const; private: std::pair, Ref> dict_lookup_delete(Ref dict, td::ConstBitPtr key, int n) const; @@ -219,6 +246,8 @@ class DictionaryFixed : public DictionaryBase { const combine_func_t& combine_func, int mode = 0, int skip1 = 0, int skip2 = 0) const; bool dict_scan_diff(Ref dict1, Ref dict2, td::BitPtr key_buffer, int n, int total_key_len, const scan_diff_func_t& diff_func, int mode = 0, int skip1 = 0, int skip2 = 0) const; + bool dict_validate_check(Ref dict, td::BitPtr key_buffer, int n, int total_key_len, + const foreach_func_t& foreach_func, bool invert_first = false) const; }; class Dictionary final : public DictionaryFixed { @@ -282,6 +311,9 @@ class Dictionary final : public DictionaryFixed { } private: + bool check_fork(CellSlice& cs, Ref c1, Ref c2, int n) const override { + return cs.empty_ext(); + } static Ref extract_value_ref(Ref cs); std::pair, int> dict_filter(Ref dict, td::BitPtr key, int n, const filter_func_t& check_leaf) const; }; @@ -341,6 +373,7 @@ class AugmentedDictionary final : public DictionaryFixed { bool check_for_each_extra(const foreach_extra_func_t& foreach_extra_func, bool invert_first = false); std::pair, Ref> traverse_extra(td::BitPtr key_buffer, int key_len, const traverse_func_t& traverse_node); + bool validate_check_extra(const foreach_extra_func_t& foreach_extra_func, bool invert_first = false); bool validate() override; template Ref lookup(const T& key) { @@ -379,10 +412,12 @@ class AugmentedDictionary final : public DictionaryFixed { private: bool compute_root() const; Ref get_node_extra(Ref cell_ref, int n) const; - std::pair, bool> dict_set(Ref dict, td::ConstBitPtr key, int n, const CellSlice& value, - SetMode mode = SetMode::Set) const; + bool check_leaf(CellSlice& cs, td::ConstBitPtr key, int key_len) const override; + bool check_fork(CellSlice& cs, Ref c1, Ref c2, int n) const override; Ref finish_create_leaf(CellBuilder& cb, const CellSlice& value) const override; Ref finish_create_fork(CellBuilder& cb, Ref c1, Ref c2, int n) const override; + std::pair, bool> dict_set(Ref dict, td::ConstBitPtr key, int n, const CellSlice& value, + SetMode mode = SetMode::Set) const; int label_mode() const override { return dict::LabelParser::chk_size; } diff --git a/ton-test-liteclient-full/lite-client/keys/keys.cpp b/ton-test-liteclient-full/lite-client/keys/keys.cpp index ca353e1..fbecd25 100644 --- a/ton-test-liteclient-full/lite-client/keys/keys.cpp +++ b/ton-test-liteclient-full/lite-client/keys/keys.cpp @@ -113,6 +113,47 @@ PublicKeyHash PrivateKey::compute_short_id() const { return PublicKeyHash{res}; } +td::SecureString PrivateKey::export_as_slice() const { + td::SecureString res; + priv_key_.visit([&](auto &obj) { res = obj.export_as_slice(); }); + return res; +} + +bool PrivateKey::exportable() const { + bool res; + priv_key_.visit([&](auto &obj) { res = obj.exportable(); }); + return res; +} + +td::Result PrivateKey::import(td::Slice s) { + if (s.size() < 4) { + return td::Status::Error(ErrorCode::protoviolation, "too short key"); + } + td::int32 id; + td::MutableSlice{reinterpret_cast(&id), 4}.copy_from(s.copy().truncate(4)); + s.remove_prefix(4); + switch (id) { + case ton_api::pk_ed25519::ID: { + auto R = privkeys::Ed25519::import(s); + if (R.is_error()) { + return R.move_as_error(); + } else { + return R.move_as_ok(); + } + } break; + case ton_api::pk_aes::ID: { + auto R = privkeys::AES::import(s); + if (R.is_error()) { + return R.move_as_error(); + } else { + return R.move_as_ok(); + } + } break; + default: + return td::Status::Error(ErrorCode::protoviolation, PSTRING() << "unknown magic " << id); + } +} + tl_object_ptr PrivateKey::tl() const { tl_object_ptr res; priv_key_.visit([&](auto &obj) { res = obj.tl(); }); diff --git a/ton-test-liteclient-full/lite-client/keys/keys.hpp b/ton-test-liteclient-full/lite-client/keys/keys.hpp index 28a714c..3228931 100644 --- a/ton-test-liteclient-full/lite-client/keys/keys.hpp +++ b/ton-test-liteclient-full/lite-client/keys/keys.hpp @@ -8,6 +8,7 @@ #include "td/actor/actor.h" #include "crypto/common/bitstring.h" #include "crypto/Ed25519.h" +#include "common/errorcode.h" namespace ton { @@ -249,6 +250,11 @@ class Ed25519 { data_ = obj.key_; } Ed25519(td::Bits256 id) : data_(id) { + id.set_zero_s(); + } + Ed25519(td::Slice data) { + CHECK(data.size() == 32); + data_.as_slice().copy_from(data); } Ed25519() { } @@ -256,6 +262,22 @@ class Ed25519 { td::Ed25519::PrivateKey export_key() { return td::Ed25519::PrivateKey{td::SecureString(data_.as_slice())}; } + td::SecureString export_as_slice() const { + td::SecureString s{36}; + auto id = ton_api::pk_ed25519::ID; + s.as_mutable_slice().copy_from(td::Slice{reinterpret_cast(&id), 4}); + s.as_mutable_slice().remove_prefix(4).copy_from(data_.as_slice()); + return s; + } + bool exportable() const { + return true; + } + static td::Result import(td::Slice slice) { + if (slice.size() != 32) { + return td::Status::Error(ErrorCode::error, "bad length"); + } + return Ed25519{slice}; + } tl_object_ptr tl() const { return create_tl_object(data_); } @@ -279,6 +301,22 @@ class AES { CHECK(data.size() == 32); data_.as_slice().copy_from(data); } + td::SecureString export_as_slice() const { + td::SecureString s{40}; + auto id = ton_api::pk_aes::ID; + s.as_mutable_slice().copy_from(td::Slice{reinterpret_cast(&id), 4}); + s.as_mutable_slice().remove_prefix(4).copy_from(data_.as_slice()); + return s; + } + bool exportable() const { + return true; + } + static td::Result import(td::Slice slice) { + if (slice.size() != 32) { + return td::Status::Error(ErrorCode::error, "bad length"); + } + return AES{slice}; + } tl_object_ptr tl() const { return create_tl_object(data_); } @@ -301,6 +339,12 @@ class Unenc { Unenc(const Unenc &obj) { data_ = obj.data_.clone(); } + td::SecureString export_as_slice() const { + UNREACHABLE(); + } + bool exportable() const { + return false; + } tl_object_ptr tl() const { return create_tl_object(data_.clone()); } @@ -323,6 +367,12 @@ class Overlay { Overlay(const Overlay &obj) { data_ = obj.data_.clone(); } + td::SecureString export_as_slice() const { + UNREACHABLE(); + } + bool exportable() const { + return false; + } tl_object_ptr tl() const { return create_tl_object(data_.clone()); } @@ -340,6 +390,12 @@ class PrivateKey { private: class Empty { public: + td::SecureString export_as_slice() const { + UNREACHABLE(); + } + bool exportable() const { + return false; + } tl_object_ptr tl() const { UNREACHABLE(); } @@ -364,6 +420,9 @@ class PrivateKey { PublicKey compute_public_key() const; PublicKeyHash compute_short_id() const; + td::SecureString export_as_slice() const; + static td::Result import(td::Slice s); + bool exportable() const; tl_object_ptr tl() const; td::Result> create_decryptor() const; diff --git a/ton-test-liteclient-full/lite-client/lite-client/lite-client.cpp b/ton-test-liteclient-full/lite-client/lite-client/lite-client.cpp index 8ff5bf4..4a6ea66 100644 --- a/ton-test-liteclient-full/lite-client/lite-client/lite-client.cpp +++ b/ton-test-liteclient-full/lite-client/lite-client/lite-client.cpp @@ -17,6 +17,7 @@ #include "ton/lite-tl.hpp" #include "block/block-db.h" #include "block/block.h" +#include "block/block-parse.h" #include "block/block-auto.h" #include "block/mc-config.h" #include "block/check-proof.h" @@ -71,14 +72,14 @@ void TestNode::run() { auto G = td::read_file(global_config_).move_as_ok(); auto gc_j = td::json_decode(G.as_slice()).move_as_ok(); - ton::ton_api::config_global gc; + ton::ton_api::liteclient_config_global gc; ton::ton_api::from_json(gc, gc_j.get_object()).ensure(); - CHECK(gc.liteclients_.size() > 0); + CHECK(gc.liteservers_.size() > 0); auto idx = - liteserver_idx_ >= 0 ? liteserver_idx_ : td::Random::fast(0, static_cast(gc.liteclients_.size() - 1)); - CHECK(idx >= 0 && static_cast(idx) <= gc.liteclients_.size()); - auto& cli = gc.liteclients_[idx]; + liteserver_idx_ >= 0 ? liteserver_idx_ : td::Random::fast(0, static_cast(gc.liteservers_.size() - 1)); + CHECK(idx >= 0 && static_cast(idx) <= gc.liteservers_.size()); + auto& cli = gc.liteservers_[idx]; td::IPAddress addr; addr.init_host_port(td::IPAddress::ipv4_to_str(cli->ip_), cli->port_).ensure(); td::TerminalIO::out() << "using liteserver " << idx << " with addr " << addr << "\n"; @@ -161,7 +162,7 @@ bool TestNode::complete_blkid(ton::BlockId partial_blkid, ton::BlockIdExt& compl bool TestNode::get_server_time() { auto b = ton::serialize_tl_object(ton::create_tl_object(), true); - return envelope_send_query(std::move(b), [&, Self = actor_id(this) ](td::Result res)->void { + return envelope_send_query(std::move(b), [&, Self = actor_id(this)](td::Result res) -> void { if (res.is_error()) { LOG(ERROR) << "cannot get server time"; return; @@ -180,7 +181,7 @@ bool TestNode::get_server_time() { bool TestNode::get_server_mc_block_id() { auto b = ton::serialize_tl_object(ton::create_tl_object(), true); - return envelope_send_query(std::move(b), [Self = actor_id(this)](td::Result res)->void { + return envelope_send_query(std::move(b), [Self = actor_id(this)](td::Result res) -> void { if (res.is_error()) { LOG(ERROR) << "cannot get masterchain info from server"; return; @@ -223,7 +224,7 @@ void TestNode::got_server_mc_block_id(ton::BlockIdExt blkid, ton::ZeroStateIdExt bool TestNode::request_block(ton::BlockIdExt blkid) { auto b = ton::serialize_tl_object( ton::create_tl_object(ton::create_tl_lite_block_id(blkid)), true); - return envelope_send_query(std::move(b), [ Self = actor_id(this), blkid ](td::Result res)->void { + return envelope_send_query(std::move(b), [Self = actor_id(this), blkid](td::Result res) -> void { if (res.is_error()) { LOG(ERROR) << "cannot obtain block " << blkid.to_str() << " from server"; return; @@ -248,7 +249,7 @@ bool TestNode::request_block(ton::BlockIdExt blkid) { bool TestNode::request_state(ton::BlockIdExt blkid) { auto b = ton::serialize_tl_object( ton::create_tl_object(ton::create_tl_lite_block_id(blkid)), true); - return envelope_send_query(std::move(b), [ Self = actor_id(this), blkid ](td::Result res)->void { + return envelope_send_query(std::move(b), [Self = actor_id(this), blkid](td::Result res) -> void { if (res.is_error()) { LOG(ERROR) << "cannot obtain state " << blkid.to_str() << " from server"; return; @@ -779,7 +780,7 @@ bool TestNode::get_account_state(ton::WorkchainId workchain, ton::StdSmcAddress LOG(INFO) << "requesting account state for " << workchain << ":" << addr.to_hex() << " with respect to " << ref_blkid.to_str(); return envelope_send_query( - std::move(b), [ Self = actor_id(this), workchain, addr, ref_blkid ](td::Result R)->void { + std::move(b), [Self = actor_id(this), workchain, addr, ref_blkid](td::Result R) -> void { if (R.is_error()) { return; } @@ -813,7 +814,7 @@ bool TestNode::get_one_transaction(ton::BlockIdExt blkid, ton::WorkchainId workc LOG(INFO) << "requesting transaction " << lt << " of " << workchain << ":" << addr.to_hex() << " from block " << blkid.to_str(); return envelope_send_query( - std::move(b), [ Self = actor_id(this), workchain, addr, lt, blkid, dump ](td::Result R)->void { + std::move(b), [Self = actor_id(this), workchain, addr, lt, blkid, dump](td::Result R) -> void { if (R.is_error()) { return; } @@ -839,7 +840,7 @@ bool TestNode::get_last_transactions(ton::WorkchainId workchain, ton::StdSmcAddr LOG(INFO) << "requesting " << count << " last transactions from " << lt << ":" << hash.to_hex() << " of " << workchain << ":" << addr.to_hex(); return envelope_send_query( - std::move(b), [ Self = actor_id(this), workchain, addr, lt, hash, count, dump ](td::Result R) { + std::move(b), [Self = actor_id(this), workchain, addr, lt, hash, count, dump](td::Result R) { if (R.is_error()) { return; } @@ -1147,7 +1148,7 @@ bool TestNode::get_block_transactions(ton::BlockIdExt blkid, int mode, unsigned true); LOG(INFO) << "requesting " << count << " transactions from block " << blkid.to_str() << " starting from account " << acc_addr.to_hex() << " lt " << lt; - return envelope_send_query(std::move(b), [ Self = actor_id(this), mode ](td::Result R) { + return envelope_send_query(std::move(b), [Self = actor_id(this), mode](td::Result R) { if (R.is_error()) { return; } @@ -1195,7 +1196,7 @@ bool TestNode::get_all_shards(bool use_last, ton::BlockIdExt blkid) { auto b = ton::serialize_tl_object( ton::create_tl_object(ton::create_tl_lite_block_id(blkid)), true); LOG(INFO) << "requesting recent shard configuration"; - return envelope_send_query(std::move(b), [Self = actor_id(this)](td::Result R)->void { + return envelope_send_query(std::move(b), [Self = actor_id(this)](td::Result R) -> void { if (R.is_error()) { return; } @@ -1253,7 +1254,7 @@ bool TestNode::get_block(ton::BlockIdExt blkid, bool dump) { auto b = ton::serialize_tl_object( ton::create_tl_object(ton::create_tl_lite_block_id(blkid)), true); return envelope_send_query( - std::move(b), [ Self = actor_id(this), blkid, dump ](td::Result res)->void { + std::move(b), [Self = actor_id(this), blkid, dump](td::Result res) -> void { if (res.is_error()) { LOG(ERROR) << "cannot obtain block " << blkid.to_str() << " from server : " << res.move_as_error().to_string(); @@ -1282,7 +1283,7 @@ bool TestNode::get_state(ton::BlockIdExt blkid, bool dump) { auto b = ton::serialize_tl_object( ton::create_tl_object(ton::create_tl_lite_block_id(blkid)), true); return envelope_send_query( - std::move(b), [ Self = actor_id(this), blkid, dump ](td::Result res)->void { + std::move(b), [Self = actor_id(this), blkid, dump](td::Result res) -> void { if (res.is_error()) { LOG(ERROR) << "cannot obtain state " << blkid.to_str() << " from server : " << res.move_as_error().to_string(); @@ -1422,7 +1423,7 @@ bool TestNode::get_block_header(ton::BlockIdExt blkid, int mode) { LOG(INFO) << "got block header request for " << blkid.to_str() << " with mode " << mode; auto b = ton::serialize_tl_object( ton::create_tl_object(ton::create_tl_lite_block_id(blkid), mode), true); - return envelope_send_query(std::move(b), [ Self = actor_id(this), blkid ](td::Result res)->void { + return envelope_send_query(std::move(b), [Self = actor_id(this), blkid](td::Result res) -> void { if (res.is_error()) { LOG(ERROR) << "cannot obtain block header for " << blkid.to_str() << " from server : " << res.move_as_error().to_string(); @@ -1453,7 +1454,7 @@ bool TestNode::lookup_block(ton::ShardIdFull shard, int mode, td::uint64 arg) { mode, ton::create_tl_lite_block_id_simple(id), arg, (td::uint32)arg), true); return envelope_send_query( - std::move(b), [ Self = actor_id(this), id, mode, arg ](td::Result res)->void { + std::move(b), [Self = actor_id(this), id, mode, arg](td::Result res) -> void { if (res.is_error()) { LOG(ERROR) << "cannot look up block header for " << id.to_str() << " with mode " << mode << " and argument " << arg << " from server : " << res.move_as_error().to_string(); @@ -1596,14 +1597,12 @@ int main(int argc, char* argv[]) { return td::Status::OK(); }); p.add_option('d', "daemonize", "set SIGHUP", [&]() { - td::set_signal_handler(td::SignalType::HangUp, - [](int sig) { + td::set_signal_handler(td::SignalType::HangUp, [](int sig) { #if TD_DARWIN || TD_LINUX - close(0); - setsid(); + close(0); + setsid(); #endif - }) - .ensure(); + }).ensure(); return td::Status::OK(); }); #if TD_DARWIN || TD_LINUX diff --git a/ton-test-liteclient-full/lite-client/tdutils/CMakeLists.txt b/ton-test-liteclient-full/lite-client/tdutils/CMakeLists.txt index 13a72e2..6c2b146 100644 --- a/ton-test-liteclient-full/lite-client/tdutils/CMakeLists.txt +++ b/ton-test-liteclient-full/lite-client/tdutils/CMakeLists.txt @@ -99,6 +99,8 @@ set(TDUTILS_SOURCE td/utils/MpmcQueue.cpp td/utils/OptionsParser.cpp td/utils/Random.cpp + td/utils/Slice.cpp + td/utils/SharedSlice.cpp td/utils/StackAllocator.cpp td/utils/Status.cpp td/utils/StringBuilder.cpp @@ -303,7 +305,7 @@ if (CRC32C_FOUND) endif() if (ABSL_FOUND) #target_link_libraries(tdutils PRIVATE absl::base absl::time) - target_link_libraries_system(tdutils absl::base absl::container absl::hash ) + target_link_libraries_system(tdutils absl::flat_hash_map absl::flat_hash_set absl::hash) endif() if (WIN32 AND WINGETOPT_FOUND) diff --git a/ton-test-liteclient-full/lite-client/tdutils/generate/auto/extension_to_mime_type.gperf b/ton-test-liteclient-full/lite-client/tdutils/generate/auto/extension_to_mime_type.gperf index 5f9b558..8f91fa4 100644 --- a/ton-test-liteclient-full/lite-client/tdutils/generate/auto/extension_to_mime_type.gperf +++ b/ton-test-liteclient-full/lite-client/tdutils/generate/auto/extension_to_mime_type.gperf @@ -210,6 +210,7 @@ struct extension_and_mime_type { "ecelp4800", "audio/vnd.nuera.ecelp4800" "ecelp7470", "audio/vnd.nuera.ecelp7470" "ecelp9600", "audio/vnd.nuera.ecelp9600" +"ecma", "application/ecmascript" "edm", "application/vnd.novadigm.edm" "edx", "application/vnd.novadigm.edx" "efif", "application/vnd.picsel" @@ -223,7 +224,6 @@ struct extension_and_mime_type { "eot", "application/vnd.ms-fontobject" "eps", "application/postscript" "epub", "application/epub+zip" -"es", "application/ecmascript" "es3", "application/vnd.eszigno3+xml" "esa", "application/vnd.osgi.subsystem" "esf", "application/vnd.epson.esf" @@ -312,14 +312,9 @@ struct extension_and_mime_type { "h261", "video/h261" "h263", "video/h263" "h264", "video/h264" -"h265", "video/h265" "hal", "application/vnd.hal+xml" "hbci", "application/vnd.hbci" "hdf", "application/x-hdf" -"heic", "image/heic" -"heics", "image/heic-sequence" -"heif", "image/heif" -"heifs", "image/heif-sequence" "hh", "text/x-c" "hlp", "application/winhlp" "hpgl", "application/vnd.hp-hpgl" @@ -425,7 +420,6 @@ struct extension_and_mime_type { "m3a", "audio/mpeg" "m3u", "audio/x-mpegurl" "m3u8", "application/vnd.apple.mpegurl" -"m4a", "audio/mp4" "m4u", "video/vnd.mpegurl" "m4v", "video/x-m4v" "ma", "application/mathematica" @@ -566,7 +560,7 @@ struct extension_and_mime_type { "osf", "application/vnd.yamaha.openscoreformat" "osfpvg", "application/vnd.yamaha.openscoreformat.osfpvg+xml" "otc", "application/vnd.oasis.opendocument.chart-template" -"otf", "font/otf" +"otf", "application/x-font-otf" "otg", "application/vnd.oasis.opendocument.graphics-template" "oth", "application/vnd.oasis.opendocument.text-web" "oti", "application/vnd.oasis.opendocument.image-template" @@ -605,7 +599,6 @@ struct extension_and_mime_type { "pgm", "image/x-portable-graymap" "pgn", "application/x-chess-pgn" "pgp", "application/pgp-encrypted" -"php", "text/x-php" "pic", "image/x-pict" "pkg", "application/octet-stream" "pki", "application/pkixcmp" @@ -808,7 +801,6 @@ struct extension_and_mime_type { "tfi", "application/thraud+xml" "tfm", "application/x-tex-tfm" "tga", "image/x-tga" -"tgs", "application/x-tgsticker" "thmx", "application/vnd.ms-officetheme" "tif", "image/tiff" "tiff", "image/tiff" @@ -821,8 +813,8 @@ struct extension_and_mime_type { "trm", "application/x-msterminal" "tsd", "application/timestamped-data" "tsv", "text/tab-separated-values" -"ttc", "font/collection" -"ttf", "font/ttf" +"ttc", "application/x-font-ttf" +"ttf", "application/x-font-ttf" "ttl", "text/turtle" "twd", "application/vnd.simtech-mindmapper" "twds", "application/vnd.simtech-mindmapper" @@ -917,8 +909,7 @@ struct extension_and_mime_type { "wmv", "video/x-ms-wmv" "wmx", "video/x-ms-wmx" "wmz", "application/x-ms-wmz" -"woff", "font/woff" -"woff2", "font/woff2" +"woff", "application/font-woff" "wpd", "application/vnd.wordperfect" "wpl", "application/vnd.ms-wpl" "wps", "application/vnd.ms-works" diff --git a/ton-test-liteclient-full/lite-client/tdutils/generate/auto/mime_type_to_extension.gperf b/ton-test-liteclient-full/lite-client/tdutils/generate/auto/mime_type_to_extension.gperf index 22b1bc4..f24d6b4 100644 --- a/ton-test-liteclient-full/lite-client/tdutils/generate/auto/mime_type_to_extension.gperf +++ b/ton-test-liteclient-full/lite-client/tdutils/generate/auto/mime_type_to_extension.gperf @@ -30,11 +30,12 @@ struct mime_type_and_extension { "application/docbook+xml", "dbk" "application/dssc+der", "dssc" "application/dssc+xml", "xdssc" -"application/ecmascript", "es" +"application/ecmascript", "ecma" "application/emma+xml", "emma" "application/epub+zip", "epub" "application/exi", "exi" "application/font-tdpfr", "pfr" +"application/font-woff", "woff" "application/gml+xml", "gml" "application/gpx+xml", "gpx" "application/gxf", "gxf" @@ -522,7 +523,6 @@ struct mime_type_and_extension { "application/x-font-snf", "snf" "application/x-font-ttf", "ttf" "application/x-font-type1", "pfa" -"application/x-font-woff", "woff" "application/x-freearc", "arc" "application/x-futuresplash", "spl" "application/x-gca-compressed", "gca" @@ -580,7 +580,6 @@ struct mime_type_and_extension { "application/x-tex-tfm", "tfm" "application/x-texinfo", "texinfo" "application/x-tgif", "obj" -"application/x-tgsticker", "tgs" "application/x-ustar", "ustar" "application/x-wais-source", "src" "application/x-x509-ca-cert", "der" @@ -606,7 +605,7 @@ struct mime_type_and_extension { "audio/adpcm", "adp" "audio/basic", "au" "audio/midi", "midi" -"audio/mp4", "m4a" +"audio/mp4", "mp4a" "audio/mpeg", "mp3" "audio/ogg", "oga" "audio/s3m", "s3m" @@ -641,19 +640,10 @@ struct mime_type_and_extension { "chemical/x-cml", "cml" "chemical/x-csml", "csml" "chemical/x-xyz", "xyz" -"font/collection", "ttc" -"font/otf", "otf" -"font/ttf", "ttf" -"font/woff", "woff" -"font/woff2", "woff2" "image/bmp", "bmp" "image/cgm", "cgm" "image/g3fax", "g3" "image/gif", "gif" -"image/heic", "heic" -"image/heic-sequence", "heics" -"image/heif", "heif" -"image/heif-sequence", "heifs" "image/ief", "ief" "image/jpeg", "jpg" "image/ktx", "ktx" @@ -744,7 +734,6 @@ struct mime_type_and_extension { "text/x-nfo", "nfo" "text/x-opml", "opml" "text/x-pascal", "pas" -"text/x-php", "php" "text/x-setext", "etx" "text/x-sfv", "sfv" "text/x-uuencode", "uu" @@ -755,7 +744,6 @@ struct mime_type_and_extension { "video/h261", "h261" "video/h263", "h263" "video/h264", "h264" -"video/h265", "h265" "video/jpeg", "jpgv" "video/jpm", "jpm" "video/mj2", "mj2" diff --git a/ton-test-liteclient-full/lite-client/tdutils/td/utils/DecTree.h b/ton-test-liteclient-full/lite-client/tdutils/td/utils/DecTree.h index 2f290a0..f616fb5 100644 --- a/ton-test-liteclient-full/lite-client/tdutils/td/utils/DecTree.h +++ b/ton-test-liteclient-full/lite-client/tdutils/td/utils/DecTree.h @@ -179,6 +179,9 @@ class DecTree { void remove(const KeyType &key) { root_ = remove_node(std::move(root_), key); } + void reset() { + root_ = nullptr; + } ValueType *get(const KeyType &key) { return get_node(root_, key); } diff --git a/ton-test-liteclient-full/lite-client/tdutils/td/utils/Hash.h b/ton-test-liteclient-full/lite-client/tdutils/td/utils/Hash.h index daf7f96..9acb49f 100644 --- a/ton-test-liteclient-full/lite-client/tdutils/td/utils/Hash.h +++ b/ton-test-liteclient-full/lite-client/tdutils/td/utils/Hash.h @@ -28,7 +28,7 @@ class Hasher { template static Hasher combine(Hasher hasher, const std::pair &value) { hasher = AbslHashValue(std::move(hasher), value.first); - hasher = AbslHashValue(std::move(hasher), value.first); + hasher = AbslHashValue(std::move(hasher), value.second); return hasher; } diff --git a/ton-test-liteclient-full/lite-client/tdutils/td/utils/SharedSlice.cpp b/ton-test-liteclient-full/lite-client/tdutils/td/utils/SharedSlice.cpp new file mode 100644 index 0000000..62e61fa --- /dev/null +++ b/ton-test-liteclient-full/lite-client/tdutils/td/utils/SharedSlice.cpp @@ -0,0 +1,8 @@ +#include "td/utils/SharedSlice.h" +#include "td/utils/buffer.h" + +namespace td { +td::BufferSlice SharedSlice::clone_as_buffer_slice() const { + return td::BufferSlice{as_slice().str()}; +} +} // namespace td diff --git a/ton-test-liteclient-full/lite-client/tdutils/td/utils/SharedSlice.h b/ton-test-liteclient-full/lite-client/tdutils/td/utils/SharedSlice.h index 520204b..523cc7a 100644 --- a/ton-test-liteclient-full/lite-client/tdutils/td/utils/SharedSlice.h +++ b/ton-test-liteclient-full/lite-client/tdutils/td/utils/SharedSlice.h @@ -1,10 +1,10 @@ #pragma once #include "td/utils/Slice.h" -#include "td/utils/buffer.h" #include #include namespace td { +class BufferSlice; namespace detail { struct SharedSliceHeader { explicit SharedSliceHeader(size_t size) : refcnt_{1}, size_{0} { @@ -117,9 +117,7 @@ class UnsafeSharedSlice { auto header = reinterpret_cast(ptr); if (header->dec()) { if (zero_on_destruct) { - auto sz = sizeof(HeaderT) + header->size(); - //TODO: better cleanup - memset(ptr, 0, sz); + MutableSlice(ptr, sizeof(HeaderT) + header->size()).fill_zero_secure(); } std::default_delete()(ptr); } @@ -154,9 +152,7 @@ class SharedSlice { return impl_.as_slice(); } - td::BufferSlice clone_as_buffer_slice() const { - return td::BufferSlice{as_slice().str()}; - } + td::BufferSlice clone_as_buffer_slice() const; operator Slice() const { return as_slice(); @@ -340,4 +336,17 @@ class UniqueSliceImpl { using UniqueSlice = UniqueSliceImpl; using SecureString = UniqueSliceImpl; + +inline MutableSlice as_mutable_slice(UniqueSharedSlice &unique_shared_slice) { + return unique_shared_slice.as_mutable_slice(); +} + +inline MutableSlice as_mutable_slice(UniqueSlice &unique_slice) { + return unique_slice.as_mutable_slice(); +} + +inline MutableSlice as_mutable_slice(SecureString &secure_string) { + return secure_string.as_mutable_slice(); +} + } // namespace td diff --git a/ton-test-liteclient-full/lite-client/tdutils/td/utils/Slice-decl.h b/ton-test-liteclient-full/lite-client/tdutils/td/utils/Slice-decl.h index 3185e8e..d4d3f7e 100644 --- a/ton-test-liteclient-full/lite-client/tdutils/td/utils/Slice-decl.h +++ b/ton-test-liteclient-full/lite-client/tdutils/td/utils/Slice-decl.h @@ -47,6 +47,9 @@ class MutableSlice { MutableSlice substr(size_t from, size_t size) const; size_t find(char c) const; size_t rfind(char c) const; + void fill(char c); + void fill_zero(); + void fill_zero_secure(); void copy_from(Slice from); diff --git a/ton-test-liteclient-full/lite-client/tdutils/td/utils/Slice.cpp b/ton-test-liteclient-full/lite-client/tdutils/td/utils/Slice.cpp new file mode 100644 index 0000000..8f4644d --- /dev/null +++ b/ton-test-liteclient-full/lite-client/tdutils/td/utils/Slice.cpp @@ -0,0 +1,25 @@ +#include "td/utils/Slice.h" +#if TD_HAVE_OPENSSL +#include +#endif + +namespace td { + +void MutableSlice::fill(char c) { + std::memset(data(), c, size()); +} +void MutableSlice::fill_zero() { + fill(0); +} +void MutableSlice::fill_zero_secure() { +#if TD_HAVE_OPENSSL + OPENSSL_cleanse(begin(), size()); +#else + volatile char *ptr = begin(); + for (size_t i = 0; i < size(); i++) { + ptr[i] = 0; + } +#endif +} + +} // namespace td diff --git a/ton-test-liteclient-full/lite-client/tdutils/td/utils/Slice.h b/ton-test-liteclient-full/lite-client/tdutils/td/utils/Slice.h index 31913db..7f4ae8a 100644 --- a/ton-test-liteclient-full/lite-client/tdutils/td/utils/Slice.h +++ b/ton-test-liteclient-full/lite-client/tdutils/td/utils/Slice.h @@ -310,4 +310,12 @@ inline MutableSlice as_slice(string &str) { return str; } +inline MutableSlice as_mutable_slice(MutableSlice slice) { + return slice; +} + +inline MutableSlice as_mutable_slice(string &str) { + return str; +} + } // namespace td diff --git a/ton-test-liteclient-full/lite-client/tdutils/td/utils/buffer.h b/ton-test-liteclient-full/lite-client/tdutils/td/utils/buffer.h index 1f3c1b5..b5d8196 100644 --- a/ton-test-liteclient-full/lite-client/tdutils/td/utils/buffer.h +++ b/ton-test-liteclient-full/lite-client/tdutils/td/utils/buffer.h @@ -791,5 +791,8 @@ inline Slice as_slice(const BufferSlice &value) { inline MutableSlice as_slice(BufferSlice &value) { return value.as_slice(); } +inline MutableSlice as_mutable_slice(BufferSlice &value) { + return value.as_slice(); +} } // namespace td diff --git a/ton-test-liteclient-full/lite-client/tdutils/td/utils/filesystem.cpp b/ton-test-liteclient-full/lite-client/tdutils/td/utils/filesystem.cpp index 4eec9ec..310e4c1 100644 --- a/ton-test-liteclient-full/lite-client/tdutils/td/utils/filesystem.cpp +++ b/ton-test-liteclient-full/lite-client/tdutils/td/utils/filesystem.cpp @@ -26,6 +26,10 @@ template <> BufferSlice create_empty(size_t size) { return BufferSlice{size}; } +template <> +SecureString create_empty(size_t size) { + return SecureString{size}; +} template Result read_file_impl(CSlice path, int64 size, int64 offset) { @@ -42,7 +46,7 @@ Result read_file_impl(CSlice path, int64 size, int64 offset) { } size -= offset; auto content = create_empty(narrow_cast(size)); - TRY_RESULT(got_size, from_file.pread(as_slice(content), offset)); + TRY_RESULT(got_size, from_file.pread(as_mutable_slice(content), offset)); if (got_size != static_cast(size)) { return Status::Error("Failed to read file"); } @@ -60,6 +64,10 @@ Result read_file_str(CSlice path, int64 size, int64 offset) { return read_file_impl(path, size, offset); } +Result read_file_secure(CSlice path, int64 size, int64 offset) { + return read_file_impl(path, size, offset); +} + // Very straightforward function. Don't expect much of it. Status copy_file(CSlice from, CSlice to, int64 size) { TRY_RESULT(content, read_file(from, size)); diff --git a/ton-test-liteclient-full/lite-client/tdutils/td/utils/filesystem.h b/ton-test-liteclient-full/lite-client/tdutils/td/utils/filesystem.h index 7768c65..6bd95d9 100644 --- a/ton-test-liteclient-full/lite-client/tdutils/td/utils/filesystem.h +++ b/ton-test-liteclient-full/lite-client/tdutils/td/utils/filesystem.h @@ -2,12 +2,14 @@ #include "td/utils/buffer.h" #include "td/utils/Slice.h" +#include "td/utils/SharedSlice.h" #include "td/utils/Status.h" namespace td { Result read_file(CSlice path, int64 size = -1, int64 offset = 0); Result read_file_str(CSlice path, int64 size = -1, int64 offset = 0); +Result read_file_secure(CSlice path, int64 size = -1, int64 offset = 0); Status copy_file(CSlice from, CSlice to, int64 size = -1) TD_WARN_UNUSED_RESULT; diff --git a/ton-test-liteclient-full/lite-client/tdutils/test/misc.cpp b/ton-test-liteclient-full/lite-client/tdutils/test/misc.cpp index a2dcbd6..96c63f0 100644 --- a/ton-test-liteclient-full/lite-client/tdutils/test/misc.cpp +++ b/ton-test-liteclient-full/lite-client/tdutils/test/misc.cpp @@ -891,6 +891,9 @@ void test_hash() { test_hash({BadValue{1}, BadValue{2}}).ensure_error(); test_hash({ValueA{1}, ValueA{2}}).ensure(); test_hash({ValueB{1}, ValueB{2}}).ensure(); + test_hash>({{1, 1}, {1, 2}}).ensure(); + // FIXME: use some better hash + //test_hash>({{1, 1}, {1, 2}, {2, 1}, {2, 2}}).ensure(); } TEST(Misc, Hasher) { diff --git a/ton-test-liteclient-full/lite-client/tl-utils/common-utils.hpp b/ton-test-liteclient-full/lite-client/tl-utils/common-utils.hpp index 57d7798..5cb4584 100644 --- a/ton-test-liteclient-full/lite-client/tl-utils/common-utils.hpp +++ b/ton-test-liteclient-full/lite-client/tl-utils/common-utils.hpp @@ -16,6 +16,11 @@ td::BufferSlice serialize_tl_object(const tl_object_ptr &T, bool boxed, td:: return serialize_tl_object(T.get(), boxed, std::move(suffix)); } +template +td::BufferSlice serialize_tl_object(const tl_object_ptr &T, bool boxed, td::Slice suffix) { + return serialize_tl_object(T.get(), boxed, std::move(suffix)); +} + template td::UInt256 get_tl_object_sha256(const tl_object_ptr &T) { return get_tl_object_sha256(T.get()); diff --git a/ton-test-liteclient-full/lite-client/tl-utils/tl-utils.cpp b/ton-test-liteclient-full/lite-client/tl-utils/tl-utils.cpp index 32ad5cf..ea980fc 100644 --- a/ton-test-liteclient-full/lite-client/tl-utils/tl-utils.cpp +++ b/ton-test-liteclient-full/lite-client/tl-utils/tl-utils.cpp @@ -8,6 +8,10 @@ namespace ton { td::BufferSlice serialize_tl_object(const ton_api::Object *T, bool boxed, td::BufferSlice &&suffix) { + return serialize_tl_object(T, boxed, suffix.as_slice()); +} + +td::BufferSlice serialize_tl_object(const ton_api::Object *T, bool boxed, td::Slice suffix) { td::TlStorerCalcLength X; T->store(X); auto l = X.get_length() + (boxed ? 4 : 0); @@ -22,8 +26,7 @@ td::BufferSlice serialize_tl_object(const ton_api::Object *T, bool boxed, td::Bu auto S = B.as_slice(); S.remove_prefix(l); - S.copy_from(suffix.as_slice()); - suffix.clear(); + S.copy_from(suffix); return B; } @@ -60,6 +63,10 @@ td::BufferSlice serialize_tl_object(const ton_api::Function *T, bool boxed) { } td::BufferSlice serialize_tl_object(const ton_api::Function *T, bool boxed, td::BufferSlice &&suffix) { + return serialize_tl_object(T, boxed, suffix.as_slice()); +} + +td::BufferSlice serialize_tl_object(const ton_api::Function *T, bool boxed, td::Slice suffix) { CHECK(boxed); td::TlStorerCalcLength X; T->store(X); @@ -73,8 +80,7 @@ td::BufferSlice serialize_tl_object(const ton_api::Function *T, bool boxed, td:: auto S = B.as_slice(); S.remove_prefix(l); - S.copy_from(suffix.as_slice()); - suffix.clear(); + S.copy_from(suffix); return B; } diff --git a/ton-test-liteclient-full/lite-client/tl-utils/tl-utils.hpp b/ton-test-liteclient-full/lite-client/tl-utils/tl-utils.hpp index 518d91e..dd1ed7f 100644 --- a/ton-test-liteclient-full/lite-client/tl-utils/tl-utils.hpp +++ b/ton-test-liteclient-full/lite-client/tl-utils/tl-utils.hpp @@ -14,6 +14,8 @@ td::BufferSlice serialize_tl_object(const ton_api::Object *T, bool boxed); td::BufferSlice serialize_tl_object(const ton_api::Function *T, bool boxed); td::BufferSlice serialize_tl_object(const ton_api::Object *T, bool boxed, td::BufferSlice &&suffix); td::BufferSlice serialize_tl_object(const ton_api::Function *T, bool boxed, td::BufferSlice &&suffix); +td::BufferSlice serialize_tl_object(const ton_api::Object *T, bool boxed, td::Slice suffix); +td::BufferSlice serialize_tl_object(const ton_api::Function *T, bool boxed, td::Slice suffix); td::UInt256 get_tl_object_sha256(const ton_api::Object *T); diff --git a/ton-test-liteclient-full/lite-client/tl/CMakeLists.txt b/ton-test-liteclient-full/lite-client/tl/CMakeLists.txt index cb86f94..5e027c6 100644 --- a/ton-test-liteclient-full/lite-client/tl/CMakeLists.txt +++ b/ton-test-liteclient-full/lite-client/tl/CMakeLists.txt @@ -10,8 +10,8 @@ add_dependencies(tl_api tl_generate_common) target_link_libraries(tl_api tdutils) target_include_directories(tl_api PUBLIC $ - $/.. - $/../crypto/ $/generate) + $ + $ $) set_source_files_properties(${TL_LITE_API} PROPERTIES GENERATED TRUE) add_library(tl_lite_api STATIC @@ -23,8 +23,8 @@ add_dependencies(tl_lite_api tl_generate_common) target_link_libraries(tl_lite_api tdutils) target_include_directories(tl_lite_api PUBLIC $ - $/.. - $/../crypto/ $/generate) + $ + $ $) if (TONLIB_ENABLE_JNI) set(TL_JNI_OBJECT @@ -44,11 +44,11 @@ target_link_libraries(tl_tonlib_api tdutils) target_link_libraries(tl_tonlib_api_json tdutils tl_tonlib_api) target_include_directories(tl_tonlib_api PUBLIC $ - $/.. - $/../crypto/ - $/generate ) + $ + $ + $ ) target_include_directories(tl_tonlib_api_json PUBLIC $ - $/.. - $/../crypto/ - $/generate ) + $ + $ + $ ) diff --git a/ton-test-liteclient-full/lite-client/tl/generate/generate_common.cpp b/ton-test-liteclient-full/lite-client/tl/generate/generate_common.cpp index 5f7030a..dbfad54 100644 --- a/ton-test-liteclient-full/lite-client/tl/generate/generate_common.cpp +++ b/ton-test-liteclient-full/lite-client/tl/generate/generate_common.cpp @@ -43,7 +43,7 @@ int main() { #ifdef TONLIB_ENABLE_JNI generate_cpp( - "auto/tl", "tonlib_api", "std::string", "std::string", "std::string", "std::string", + "auto/tl", "tonlib_api", "std::string", "std::string", "td::SecureString", "td::SecureString", {"\"tl/tl_jni_object.h\"", "\"tl/tl_object_store.h\"", "\"td/utils/int_types.h\""}, {"", "\"td/utils/SharedSlice.h\""}); #else diff --git a/ton-test-liteclient-full/lite-client/tl/generate/scheme/ton_api.tl b/ton-test-liteclient-full/lite-client/tl/generate/scheme/ton_api.tl index 47d717e..8c8381f 100644 --- a/ton-test-liteclient-full/lite-client/tl/generate/scheme/ton_api.tl +++ b/ton-test-liteclient-full/lite-client/tl/generate/scheme/ton_api.tl @@ -22,6 +22,10 @@ testVectorBytes value:(vector bytes) = TestObject; tcp.pong random_id:long = tcp.Pong; +tcp.authentificate nonce:bytes = tcp.Message; +tcp.authentificationNonce nonce:bytes = tcp.Message; +tcp.authentificationComplete key:PublicKey signature:bytes = tcp.Message; + fec.raptorQ data_size:int symbol_size:int symbols_count:int = fec.Type; fec.roundRobin data_size:int symbol_size:int symbols_count:int = fec.Type; fec.online data_size:int symbol_size:int symbols_count:int = fec.Type; @@ -60,7 +64,7 @@ adnl.address.udp6 ip:int128 port:int = adnl.Address; adnl.addressList addrs:(vector adnl.Address) version:int reinit_date:int priority:int expire_at:int = adnl.AddressList; -adnl.node id:PublicKey addr_list:adnl.addressList signature:bytes = adnl.Node; +adnl.node id:PublicKey addr_list:adnl.addressList = adnl.Node; adnl.nodes nodes:(vector adnl.node) = adnl.Nodes; ---functions--- @@ -280,7 +284,7 @@ hashable.vector value:(vector int) = Hashable; hashable.validatorSessionOldRound seqno:int block:int signatures:int = Hashable; hashable.validatorSessionRoundAttempt seqno:int votes:int precommitted:int = Hashable; hashable.validatorSessionRound locked_round:int locked_block:int seqno:int precommitted:Bool - approved_blocks:int signatures:int attempts:int = Hashable; + first_attempt:int approved_blocks:int signatures:int attempts:int = Hashable; hashable.blockSignature signature:int = Hashable; hashable.sentBlock src:int root_hash:int file_hash:int collated_data_file_hash:int = Hashable; hashable.sentBlockEmpty = Hashable; @@ -457,26 +461,89 @@ db.lt.status.value total_shards:int = db.lt.status.Value; ---types--- -id.config.local id:PrivateKey addr_list:adnl.addressList = id.config.Local; +id.config.local id:PrivateKey = id.config.Local; dht.config.local id:adnl.id.short = dht.config.Local; -dht.config.random.local cnt:int addr_list:adnl.addressList = dht.config.Local; -overlay.config.local name:bytes id:adnl.id.short = overlay.config.Local; -overlay.config.random.local cnt:int name:bytes addr_list:adnl.addressList = overlay.config.Local; -catchain.config.local tag:int256 id:adnl.id.short = catchain.config.Local; +dht.config.random.local cnt:int = dht.config.Local; liteserver.config.local id:PrivateKey port:int = liteserver.config.Local; -dummyworkchain0.config.local id:adnl.id.short = dummyworkchain0.config.Local; +liteserver.config.random.local port:int = liteserver.config.Local; validator.config.local id:adnl.id.short = validator.config.Local; validator.config.random.local addr_list:adnl.addressList = validator.config.Local; -config.local udp_ports:(vector int) tcp_ports:(vector int) local_ids:(vector id.config.local) dht:(vector dht.config.Local) public_overlays:(vector overlay.config.Local) catchains:(vector catchain.config.local) dummy0:(vector dummyworkchain0.config.local) validators:(vector validator.config.Local) liteservers:(vector liteserver.config.local) = config.Local; +control.config.local priv:PrivateKey pub:int256 port:int = control.config.Local; +config.local local_ids:(vector id.config.local) dht:(vector dht.config.Local) validators:(vector validator.config.Local) liteservers:(vector liteserver.config.Local) control:(vector control.config.local) = config.Local; dht.config.global static_nodes:dht.nodes k:int a:int = dht.config.Global; adnl.config.global static_nodes:adnl.nodes = adnl.config.Global; catchain.config.global tag:int256 nodes:(vector PublicKey) = catchain.config.Global; -liteclient.config.global id:PublicKey ip:int port:int = liteserver.config.Global; dummyworkchain0.config.global zero_state_hash:int256 = dummyworkchain0.config.Global; -validator.config.global zero_state_root_hash:int256 zero_state_file_hash:int256 = validator.config.Global; -config.global adnl:adnl.config.global dht:dht.config.global catchains:(vector catchain.config.global) dummy0:(vector dummyworkchain0.config.global) - validators:(vector validator.config.global) liteclients:(vector liteclient.config.global) = config.Global; +validator.config.global zero_state:tonNode.blockIdExt init_block:tonNode.blockIdExt = validator.config.Global; +config.global adnl:adnl.config.global dht:dht.config.global validator:validator.config.global = config.Global; + +liteserver.desc id:PublicKey ip:int port:int = liteserver.Desc; +liteclient.config.global liteservers:(vector liteserver.desc) = liteclient.config.Global; + +engine.adnl id:int256 category:int = engine.Adnl; +engine.addr ip:int port:int is_proxy:Bool categories:(vector int) priority_categories:(vector int) = engine.Addr; +engine.dht id:int256 = engine.Dht; +engine.fullNode id:int256 adnl:int256 expire_at:int = engine.FullNode; +engine.validatorTempKey key:int256 expire_at:int = engine.ValidatorTempKey; +engine.validatorAdnlAddress id:int256 expire_at:int = engine.ValidatorAdnlAddress; +engine.validator id:int256 temp_keys:(vector engine.validatorTempKey) adnl_addrs:(vector engine.validatorAdnlAddress) expire_at:int = engine.Validator; +engine.liteServer id:int256 port:int = engine.LiteServer; +engine.controlProcess id:int256 permissions:int = engine.ControlProcess; +engine.controlInterface id:int256 port:int allowed:(vector engine.controlProcess) = engine.ControlInterface; +engine.gc ids:(vector int256) = engine.Gc; + +engine.dht.config dht:(vector engine.dht) gc:engine.gc = engine.dht.Config; +engine.validator.config addrs:(vector engine.addr) adnl:(vector engine.adnl) + dht:(vector engine.dht) fullnode:engine.fullNode + validators:(vector engine.validator) liteservers:(vector engine.liteServer) + control:(vector engine.controlInterface) + gc:engine.gc = engine.validator.Config; + +---functions--- + +---types--- + +engine.validator.keyHash key_hash:int256 = engine.validator.KeyHash; +engine.validator.signature signature:bytes = engine.validator.Signature; + +engine.validator.oneStat key:string value:string = engine.validator.OneStat; +engine.validator.stats stats:(vector engine.validator.oneStat) = engine.validator.Stats; + +engine.validator.controlQueryError code:int message:string = engine.validator.ControlQueryError; + +engine.validator.time time:int = engine.validator.Time; +engine.validator.success = engine.validator.Success; ---functions--- +engine.validator.setPassword password:bytes = engine.validator.Success; +engine.validator.changePassword old_password:bytes new_password:bytes = engine.validator.Success; + +engine.validator.getTime = engine.validator.Time; +engine.validator.importLocalKey key_hash:int256 = engine.validator.KeyHash; +engine.validator.importPrivateKey key:PrivateKey = engine.validator.KeyHash; +engine.validator.exportPrivateKey key_hash:int256 = PrivateKey; +engine.validator.exportPublicKey key_hash:int256 = PublicKey; +engine.validator.generateKeyPair = engine.validator.KeyHash; +engine.validator.addAdnlId key_hash:int256 category:int = engine.validator.Success; +engine.validator.addDhtId key_hash:int256 = engine.validator.Success; +engine.validator.addValidatorPermanentKey key_hash:int256 ttl:int = engine.validator.Success; +engine.validator.addValidatorTempKey permanent_key_hash:int256 key_hash:int256 ttl:int = engine.validator.Success; +engine.validator.addValidatorAdnlAddress permanent_key_hash:int256 key_hash:int256 ttl:int = engine.validator.Success; +engine.validator.delAdnlId = engine.validator.Success; +engine.validator.delDhtId = engine.validator.Success; +engine.validator.delValidatorPermanentKey key_hash:int256 = engine.validator.Success; +engine.validator.delValidatorTempKey key_hash:int256 = engine.validator.Success; +engine.validator.delValidatorAdnlAddress key_hash:int256 = engine.validator.Success; +engine.validator.addListeningPort ip:int port:int categories:(vector int) priority_categories:(vector int) = engine.validator.Success; +engine.validator.addProxy ip:int port:int categories:(vector int) = engine.validator.Success; +engine.validator.delListeningPort ip:int port:int categories:(vector int) priority_categories:(vector int) = engine.validator.Success; +engine.validator.delProxy ip:int port:int categories:(vector int) = engine.validator.Success; + +engine.validator.sign data:bytes = engine.validator.Signature; + +engine.validator.getStats = engine.validator.Stats; +engine.validator.getConfig = engine.validator.Config; + +engine.validator.controlQuery data:bytes = Object; diff --git a/ton-test-liteclient-full/lite-client/tl/generate/scheme/ton_api.tlo b/ton-test-liteclient-full/lite-client/tl/generate/scheme/ton_api.tlo index 465b721d65382d602890e8e4315120e71e3cb235..d3254b8a60706238afb3e96c61c3c1610316ae24 100644 GIT binary patch delta 6095 zcmbtYeQ*=k5kJeaE!#4djfhW3wq#_2Tx473BjLlIT39YN7!buFgc$6zbl4ZmI`!^s z;{L%`W=$fx2ak{`aHtq8H7hPZr&LW-@9rKUTW2 zKe;HoGaKE=wwr%<`0g9%a;`6@bNtYDE2^I{6S?y2)~g?v585MQT#>{W3e4~&19|>j zGb_G+6?GRKN7r)gwtKnOi9$Z!E5!NQU^FU?pJHuc{`Wo8vUm*3Tqnd`^=10HW(~0| zuJF<}F2ZBIqpbDxL$hwzhb1w>%j2B0_|t!UxTQc>hWeE8rycfZKYYMetc$)f7>lhH zqkuExk%^aI&DZ5CBjZoazU$WGi5E+BNxPu%8+d7mm#}8m$IO~io37c&#e^uQh!Wzm zoSs)&S}r-$HNj{+1|jmY3=#e5tyNn|L@DbIeju@$AHjO|+*soc;y2o-)Z(bO3B3%1 zu4S{XjZ;mVi183{tuKKEKKJ+U9-gD`;I;*ji}i!NG}0nTqNJjgul?bGJ8nqxO-8z43ay>U(%lz45|;-$7NcNrI93|ZUAw~xJ_ zH2T>v7{Q5@Q83TxxOJrK3d!?yTphvys+{RWn`ZdTkDveXe)Q-JU-{dU=U!uDIAf8R zz{izZT!{DpYgfj^9xeu8cI9Z>*|>6?y=jwgsj*;3+_B=0&i9nbiTK4cyR^#n!BQa z0o5PQm9|^Zde7maqHTgCD_xwT;3`Jz`<>{TN6~QlDt-B6aTeOO*@;fN$gV4^)HVAX zeaN%djskP~bl-D)EU^LqQgg1~()f)tAVZhuXp!yr>Y;XceVW;rw?q3e<<(AWzt@kJ zRB297H~A6ZU8upU@8P8?zh-lr)H7F?hUaQwBrnX}WVJ)<%jfWAu8rfS!7%WQn%Y>@ zz94`ax-4pML?;)7)gR9;oG2=X{#c|}QLkEm3NI&_xnD&{ws!fm9P~`# zaKnXX?Lc<=zl>~dbD>3$yAqPPeFSf+Zl|NLAi^npuP6z;jP|amvCqUC#!t#f7p2&@ z!iN&O^1T?1J_veRK~=!BS`OjewtS0jKZ5GL?f|H0Q+lZB=bsvCphIJus_{8FVb7L4 zE*2Aq`6$8}3c>c2y*ajM!}YuI9OYLKD4&3 zetrZUY4*&<6PzRgHdX-X?}Cbx(Uk?|W?%uRq2I4Y30SmGnj)lpQWQZohOUC$qSk&7 zfCNBDgU}oz1)_5Z(4knZj~F+AfHqDi7~17x7?4BMCcIcZZ=7-{Iv%o2$7_P@;6G?q zkPD(C%?g0j1^6Fa>eBX-k0nxu!hFY%*}8G0yo18K@XU)fAu`Dsta8oaKgo03R)WNYDOX_YD5k% z1iiXLG(K-;epTG}ym~I^dUYA>ZYk(#yv(#uPSHNltGmN9zb*(*v~^}aD64!BbaY%f zAjY@OJqmju*&?L|t7+uSL{ip>Bwh%5KnELw0WumRGu2ca!T=unprp5FRxkw(cs*uN zBqzH+%VL3u77I}_wA5*(l2hoMpY&LR#4!kI$9EvEgg7vu*A8`YsICD>Z2ED)3K}t; z=teQo1sQrG#G)OMuvvY$81b2b7oc}~e9$KwX_5dJczMffJ1UI4m>$Or*l3$8shO|B zCEKHW3X?YyuAG|E6Ay+;fiH7|N+0Ir!Zra0Bm+E}KFyrC7>}Uw&F&V^(2@LX)t2`h z1_V60)?y2smpdXgrXw9Y^wWR2G9fjK1Bn=qcQuV>g6n|#?kzzBjq}Walr#@*cc@Gs zjqPQfz||lC(lZMl<|VNUN|r}Yha5QtLR?VLyH9$xmjm0r*21iNk`ElXX<~ssf+<8v zDnfm`jvcaQEk4ol*j3|u6`nCLD%zr{LMDza`t^6;PCZ)Ei~Bnj)_*z-uVNTC)TdzB z6mG`05==*X{#*TjtIUMfF31WW7vjBPQBtN+%?OxSz@76S6E8gdmZp3CVB*Auj4@!E z1Jn-I!^vi6_Erreb!$54?qje2(U&QdjpQ|(8qzprDJS?h^xsk-Q(PPuw@MSXiWNDj zoS>;r7M$Lf?(`?#^HTNaIt>%l)jSoMA?w7$=5!AW#D(9j`dkk>ZTKHUTA%LlIc4JO zZiB-}9~bZC!<;M+i&B)F9SOrK#-d#bU5Sn)oJH0+KRhkDUhiI#?&tK&Lr2Svezdn4 zb@npM6YYs=6yO8MKF>DVx$~aQ#v2vo@wb`VZWKO<)O}2My6%Ms>y7n}G453FdU2S9 z1p20b@&8@D@-gTaRDQ-5VL<=%!m2W3iPPwrOFSdG zq1rCrx<21n-ZZ+gyfY%NNl$g_m^5ijHNQQ98(J8@*5VGYUdh$&TMyAAj9hx)VQk~i zk1y)+7^+l98SIG(5fy~F|EX Qv!UIY;YTud_>;VU17}`t?f?J) delta 1652 zcmZuyUuaup6u(Js{;fAOWU0Bex5>?#G+!W%5wfnj+O@Wudx+36(6Yrfw@GXwx4B80 z%K9Ky7&2ty<{2Wiu6>x$i7#^1jXI%2nR_T>VjXi2PDNy39~}6$Ddyb!eQU(L2c)sH0wZ-IT8wD%BcK_nIp0;IeuwnCyKOYVp?#L^R zd`T%+l6kH2YI!=D(eg0tk@zF;46VSbgYXmoj{0HKLG+X};ovXqJ47JoS%rfhzkT}7 z^A{x;nF@f{DTB*L_|SE!8`3_~ecQe`BC6#^akf~0y;x__Ur@CvMFqvu)&Aa}vj+m( z7O=+`)&KTItb+A}D^9=GbW)XhD0m2n9d~u|M8?7IeAD}}eq730t$%U)&0x}EbOt$D zf@>!QNXae}7?fqENtwdgZA5}gulP-Li=o~ywHpgR^pxQ8Ahla!=z4hE^z`p)u_Hso zL>G3YO+oGHgYwJmAQLZmqcRM~Xl1FM0cLT$1D}Px(~rR&*~>5e`b!W-dV)JRRQ+_- zmvKTkvJ7`44bv+ZrI}7eX{K+Z5mU7lJ#PwUdx^;0F?xN}k#Ift_7%invzOMr{_{W(dPRXjw*I7Nn+MaadohFV{bPY8aT zMz>O^sL6^}C~_QrgIpu9l8r#NL5y*Qh5m7KZa4cs^vM4^v#OR?RE|R(JQyV~&@X_X z`1=rOB^YPgL+5b9n#`;`rBcS$lsPoX5-<2#hM%N#A;*P@H%*=d> z>~5_J7vSn#?O32t zYOrfjS1JW8_xKZ;$X0O}~>qm@nfwkd5gVR-sHw3v>GKEt$!eh5!x~Gi~y;?un z7F~^glVo@7k#x = raw.Transaction; +raw.transactions transactions:vector = raw.Transactions; testWallet.initialAccountState public_key:bytes = testWallet.InitialAccountState; testWallet.accountState balance:int64 seqno:int32 last_transaction_id:internal.transactionId = testWallet.AccountState; @@ -67,6 +70,7 @@ changeLocalPassword input_key:inputKey new_local_password:secureBytes = Key; raw.getAccountAddress initital_account_state:raw.initialAccountState = AccountAddress; raw.getAccountState account_address:accountAddress = raw.AccountState; raw.sendMessage destination:accountAddress initial_account_state:bytes data:bytes = Ok; +raw.getTransactions account_address:accountAddress from_transaction_id:internal.transactionId = raw.Transactions; testWallet.init private_key:inputKey = Ok; testWallet.getAccountAddress initital_account_state:testWallet.initialAccountState = AccountAddress; diff --git a/ton-test-liteclient-full/lite-client/tl/generate/scheme/tonlib_api.tlo b/ton-test-liteclient-full/lite-client/tl/generate/scheme/tonlib_api.tlo index 87581e88d6fde689f456e03bf301eda903ca4e5e..a1571a3c6f6c8b5886757f4651bf31a4408d8f1e 100644 GIT binary patch delta 569 zcmX?N{lt;?(QJJy1}IS6$a|QX<>oxY`;)IQCkR9cOx?p>lvu9kn_66)n4UUuf`mxH ztmr5FAhD35#Ju9fWp?Rj_cfW*QYZKGNouiW=Edh0r-O~-ux&dCGMf=o74&Z9Xa3&JGE_7#RygP@p1PT)OjsxiBcY(o;*2qOurfG1KHk8STjrBw1L6(~9zQ PfnlM|0yceuqzMZEEgZl^ delta 148 zcmaFjc*L6b(QJJy1}Ko*$a|QX#a8KZ_~a|h36poQTwzq)+{>EJ1{T}I^MpwTBq>mo zSgx0toSa{pR}x&3Sdt3V4icZlpCclWlUQ65Us9BqSDct!l9`_el$+eZF9R`k0e{bC b3n2w2M#;^QBJJ!D!5VoBFk|xu850%&+gC4! diff --git a/ton-test-liteclient-full/lite-client/tl/generate/tl_writer_jni_cpp.cpp b/ton-test-liteclient-full/lite-client/tl/generate/tl_writer_jni_cpp.cpp index 6552c64..9aac6d6 100644 --- a/ton-test-liteclient-full/lite-client/tl/generate/tl_writer_jni_cpp.cpp +++ b/ton-test-liteclient-full/lite-client/tl/generate/tl_writer_jni_cpp.cpp @@ -94,6 +94,8 @@ std::string TD_TL_writer_jni_cpp::gen_vector_fetch(std::string field_name, const std::string template_type; if (vector_type == string_type) { template_type = "std::string"; + } else if (vector_type == secure_string_type) { + template_type = secure_string_type; } else if (vector_type.compare(0, 11, "std::vector") == 0) { const tl::tl_tree_type *child = static_cast(t->children[0]); template_type = gen_type_name(child); @@ -146,10 +148,14 @@ std::string TD_TL_writer_jni_cpp::gen_type_fetch(const std::string &field_name, return "env->CallObjectMethod(p, td::jni::LongGetValueMethodID)"; } else if (name == "Double") { return "env->CallObjectMethod(p, td::jni::DoubleGetValueMethodID)"; - } else if (name == "String" || name == "SecureString") { + } else if (name == "String") { return "td::jni::from_jstring(env, (jstring)p)"; - } else if (name == "Bytes" || name == "SecureBytes") { + } else if (name == "Bytes") { return "td::jni::from_bytes(env, (jbyteArray)p)"; + } else if (name == "SecureString") { + return "td::jni::from_jstring_secure(env, (jstring)p)"; + } else if (name == "SecureBytes") { + return "td::jni::from_bytes_secure(env, (jbyteArray)p)"; } } @@ -161,10 +167,14 @@ std::string TD_TL_writer_jni_cpp::gen_type_fetch(const std::string &field_name, res = "env->GetLongField(p, " + field_name + "fieldID)"; } else if (name == "Double") { res = "env->GetDoubleField(p, " + field_name + "fieldID)"; - } else if (name == "String" || name == "SecureString") { + } else if (name == "String") { res = "td::jni::fetch_string(env, p, " + field_name + "fieldID)"; - } else if (name == "Bytes" || name == "SecureBytes") { + } else if (name == "Bytes") { res = "td::jni::from_bytes(env, (jbyteArray)td::jni::fetch_object(env, p, " + field_name + "fieldID))"; + } else if (name == "SecureString") { + res = "td::jni::fetch_string_secure(env, p, " + field_name + "fieldID)"; + } else if (name == "SecureBytes") { + res = "td::jni::from_bytes_secure(env, (jbyteArray)td::jni::fetch_object(env, p, " + field_name + "fieldID))"; } else if (name == "Vector") { const tl::tl_tree_type *child = static_cast(tree_type->children[0]); res = gen_vector_fetch(field_name, child, vars, parser_type); @@ -243,8 +253,8 @@ std::string TD_TL_writer_jni_cpp::gen_vector_store(const std::string &field_name assert(false); // TODO } if (vector_type == "std::int32_t" || vector_type == "std::int64_t" || vector_type == "double" || - vector_type == string_type || vector_type.compare(0, 11, "std::vector") == 0 || - vector_type.compare(0, 10, "object_ptr") == 0) { + vector_type == string_type || vector_type == secure_string_type || + vector_type.compare(0, 11, "std::vector") == 0 || vector_type.compare(0, 10, "object_ptr") == 0) { return "{ " "auto arr_tmp_ = td::jni::store_vector(env, " + field_name + @@ -286,7 +296,8 @@ std::string TD_TL_writer_jni_cpp::gen_type_store(const std::string &field_name, } std::string res; - if (name == "Int32" || name == "Int53" || name == "Int64" || name == "Double" || name == "Bool" || name == "String") { + if (name == "Int32" || name == "Int53" || name == "Int64" || name == "Double" || name == "Bool" || name == "String" || + name == "SecureString") { if (storer_type == 1) { res = "s.store_field(\"" + get_pretty_field_name(field_name) + "\", " + field_name + ");"; } else if (name == "Bool") { @@ -297,14 +308,18 @@ std::string TD_TL_writer_jni_cpp::gen_type_store(const std::string &field_name, res = "env->SetLongField(s, " + field_name + "fieldID, " + field_name + ");"; } else if (name == "Double") { res = "env->SetDoubleField(s, " + field_name + "fieldID, " + field_name + ");"; - } else if (name == "String" || name == "SecureString") { + } else if (name == "String") { res = "{ jstring nextString = td::jni::to_jstring(env, " + field_name + "); if (nextString) { env->SetObjectField(s, " + field_name + "fieldID, nextString); env->DeleteLocalRef(nextString); } }"; + } else if (name == "SecureString") { + res = "{ jstring nextString = td::jni::to_jstring_secure(env, " + field_name + + "); if (nextString) { env->SetObjectField(s, " + field_name + + "fieldID, nextString); env->DeleteLocalRef(nextString); } }"; } else { assert(false); } - } else if (name == "Bytes" || name == "SecureBytes") { + } else if (name == "Bytes") { if (storer_type == 1) { res = "s.store_bytes_field(\"" + get_pretty_field_name(field_name) + "\", " + field_name + ");"; } else { @@ -312,6 +327,14 @@ std::string TD_TL_writer_jni_cpp::gen_type_store(const std::string &field_name, "); if (nextBytes) { env->SetObjectField(s, " + field_name + "fieldID, nextBytes); env->DeleteLocalRef(nextBytes); } }"; } + } else if (name == "SecureBytes") { + if (storer_type == 1) { + res = "s.store_bytes_field(\"" + get_pretty_field_name(field_name) + "\", " + field_name + ");"; + } else { + res = "{ jbyteArray nextBytes = td::jni::to_bytes_secure(env, " + field_name + + "); if (nextBytes) { env->SetObjectField(s, " + field_name + + "fieldID, nextBytes); env->DeleteLocalRef(nextBytes); } }"; + } } else if (name == "Vector") { const tl::tl_tree_type *child = static_cast(tree_type->children[0]); res = gen_vector_store(field_name, child, vars, storer_type); diff --git a/ton-test-liteclient-full/lite-client/tl/tl/tl_jni_object.cpp b/ton-test-liteclient-full/lite-client/tl/tl/tl_jni_object.cpp index 10b819d..efb0b76 100644 --- a/ton-test-liteclient-full/lite-client/tl/tl/tl_jni_object.cpp +++ b/ton-test-liteclient-full/lite-client/tl/tl/tl_jni_object.cpp @@ -191,26 +191,42 @@ std::string fetch_string(JNIEnv *env, jobject o, jfieldID id) { return res; } -std::string from_jstring(JNIEnv *env, jstring s) { +SecureString fetch_string_secure(JNIEnv *env, jobject o, jfieldID id) { + jstring s = (jstring)env->GetObjectField(o, id); + if (s == nullptr) { + // treat null as an empty string + return {}; + } + auto res = from_jstring_secure(env, s); + env->DeleteLocalRef(s); + return res; +} + +template +T do_from_jstring(JNIEnv *env, jstring s) { if (!s) { - return ""; + return T{}; } jsize s_len = env->GetStringLength(s); const jchar *p = env->GetStringChars(s, nullptr); if (p == nullptr) { parse_error = true; - return std::string(); + return T{}; } size_t len = get_utf8_from_utf16_length(p, s_len); - std::string res(len, '\0'); + T res(len, '\0'); if (len) { - utf16_to_utf8(p, s_len, &res[0]); + utf16_to_utf8(p, s_len, as_mutable_slice(res).begin()); } env->ReleaseStringChars(s, p); return res; } -jstring to_jstring(JNIEnv *env, const std::string &s) { +std::string from_jstring(JNIEnv *env, jstring s) { + return do_from_jstring(env, s); +} + +jstring do_to_jstring(JNIEnv *env, CSlice s) { jsize surrogates = 0; jsize unicode_len = get_utf16_from_utf8_length(s.c_str(), s.size(), &surrogates); if (surrogates == 0) { @@ -229,20 +245,28 @@ jstring to_jstring(JNIEnv *env, const std::string &s) { return env->NewString(result.get(), result_len); } -std::string from_bytes(JNIEnv *env, jbyteArray arr) { - std::string b; +jstring to_jstring(JNIEnv *env, const std::string &s) { + return do_to_jstring(env, s); +} + +template +T do_from_bytes(JNIEnv *env, jbyteArray arr) { + T b; if (arr != nullptr) { jsize length = env->GetArrayLength(arr); if (length != 0) { - b.resize(narrow_cast(length)); - env->GetByteArrayRegion(arr, 0, length, reinterpret_cast(&b[0])); + b = T(narrow_cast(length), '\0'); + env->GetByteArrayRegion(arr, 0, length, reinterpret_cast(as_mutable_slice(b).begin())); } env->DeleteLocalRef(arr); } return b; } +std::string from_bytes(JNIEnv *env, jbyteArray arr) { + return do_from_bytes(env, arr); +} -jbyteArray to_bytes(JNIEnv *env, const std::string &b) { +jbyteArray to_bytes(JNIEnv *env, Slice b) { static_assert(sizeof(char) == sizeof(jbyte), "Mismatched jbyte size"); jsize length = narrow_cast(b.size()); jbyteArray arr = env->NewByteArray(length); @@ -252,6 +276,25 @@ jbyteArray to_bytes(JNIEnv *env, const std::string &b) { return arr; } +SecureString from_jstring_secure(JNIEnv *env, jstring s) { + return do_from_jstring(env, s); +} + +jstring to_jstring_secure(JNIEnv *env, Slice s) { + SecureString cstr(s.size() + 1); + cstr.as_mutable_slice().copy_from(s); + cstr.as_mutable_slice().back() = 0; + return do_to_jstring(env, CSlice(cstr.data(), cstr.data() + s.size())); +} + +SecureString from_bytes_secure(JNIEnv *env, jbyteArray arr) { + return do_from_bytes(env, arr); +} + +jbyteArray to_bytes_secure(JNIEnv *env, Slice b) { + return to_bytes(env, b); +} + jintArray store_vector(JNIEnv *env, const std::vector &v) { static_assert(sizeof(std::int32_t) == sizeof(jint), "Mismatched jint size"); jsize length = narrow_cast(v.size()); @@ -297,6 +340,21 @@ jobjectArray store_vector(JNIEnv *env, const std::vector &v) { return arr; } +jobjectArray store_vector(JNIEnv *env, const std::vector &v) { + jsize length = narrow_cast(v.size()); + jobjectArray arr = env->NewObjectArray(length, StringClass, 0); + if (arr != nullptr) { + for (jsize i = 0; i < length; i++) { + jstring str = to_jstring_secure(env, v[i]); + if (str) { + env->SetObjectArrayElement(arr, i, str); + env->DeleteLocalRef(str); + } + } + } + return arr; +} + std::vector fetch_vector(JNIEnv *env, jintArray arr) { std::vector result; if (arr != nullptr) { diff --git a/ton-test-liteclient-full/lite-client/tl/tl/tl_jni_object.h b/ton-test-liteclient-full/lite-client/tl/tl/tl_jni_object.h index 6be8e94..240e404 100644 --- a/ton-test-liteclient-full/lite-client/tl/tl/tl_jni_object.h +++ b/ton-test-liteclient-full/lite-client/tl/tl/tl_jni_object.h @@ -7,6 +7,9 @@ #include #include +#include "td/utils/Slice.h" +#include "td/utils/SharedSlice.h" + namespace td { namespace jni { @@ -58,6 +61,7 @@ class JvmThreadDetacher { std::unique_ptr get_jni_env(JavaVM *java_vm, jint jni_version); std::string fetch_string(JNIEnv *env, jobject o, jfieldID id); +SecureString fetch_string_secure(JNIEnv *env, jobject o, jfieldID id); inline jobject fetch_object(JNIEnv *env, const jobject &o, const jfieldID &id) { // null return object is implicitly allowed @@ -73,12 +77,16 @@ inline void reset_parse_error() { } std::string from_jstring(JNIEnv *env, jstring s); +SecureString from_jstring_secure(JNIEnv *env, jstring s); jstring to_jstring(JNIEnv *env, const std::string &s); +jstring to_jstring_secure(JNIEnv *env, Slice s); std::string from_bytes(JNIEnv *env, jbyteArray arr); +SecureString from_bytes_secure(JNIEnv *env, jbyteArray arr); -jbyteArray to_bytes(JNIEnv *env, const std::string &b); +jbyteArray to_bytes(JNIEnv *env, Slice b); +jbyteArray to_bytes_secure(JNIEnv *env, Slice b); void init_vars(JNIEnv *env, const char *td_api_java_package); @@ -90,6 +98,8 @@ jdoubleArray store_vector(JNIEnv *env, const std::vector &v); jobjectArray store_vector(JNIEnv *env, const std::vector &v); +jobjectArray store_vector(JNIEnv *env, const std::vector &v); + template jobjectArray store_vector(JNIEnv *env, const std::vector &v) { jint length = static_cast(v.size()); @@ -182,6 +192,26 @@ struct FetchVector { } }; +template <> +struct FetchVector { + static std::vector fetch(JNIEnv *env, jobjectArray arr) { + std::vector result; + if (arr != nullptr) { + jsize length = env->GetArrayLength(arr); + result.reserve(length); + for (jsize i = 0; i < length; i++) { + jstring str = (jstring)env->GetObjectArrayElement(arr, i); + result.push_back(jni::from_jstring_secure(env, str)); + if (str) { + env->DeleteLocalRef(str); + } + } + env->DeleteLocalRef(arr); + } + return result; + } +}; + template struct FetchVector> { static auto fetch(JNIEnv *env, jobjectArray arr) { diff --git a/ton-test-liteclient-full/lite-client/ton/interfaces/block-handle.h b/ton-test-liteclient-full/lite-client/ton/interfaces/block-handle.h deleted file mode 100644 index 35a3aee..0000000 --- a/ton-test-liteclient-full/lite-client/ton/interfaces/block-handle.h +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -#include "ton/ton-types.h" -#include "td/actor/PromiseFuture.h" -#include "td/actor/actor.h" - -namespace ton { - -class ValidatorManager; - -struct BlockHandleImpl { - public: - virtual BlockIdExt id() const = 0; - virtual bool received() const = 0; - virtual bool moved_to_storage() const = 0; - virtual bool deleted() const = 0; - virtual bool inited_next_left() const = 0; - virtual bool inited_next_right() const = 0; - virtual bool inited_next() const = 0; - virtual bool inited_prev_left() const = 0; - virtual bool inited_prev_right() const = 0; - virtual bool inited_prev() const = 0; - virtual bool inited_logical_time() const = 0; - virtual bool inited_unix_time() const = 0; - virtual bool inited_proof() const = 0; - virtual bool inited_proof_link() const = 0; - virtual bool inited_signatures() const = 0; - virtual bool inited_split_after() const = 0; - virtual bool inited_merge_before() const = 0; - virtual bool inited_is_key_block() const = 0; - virtual bool split_after() const = 0; - virtual bool merge_before() const = 0; - virtual bool is_key_block() const = 0; - virtual bool inited_state_root_hash() const = 0; - virtual bool received_state() const = 0; - virtual bool inited_state_boc() const = 0; - virtual bool deleted_state_boc() const = 0; - virtual bool need_flush() const = 0; - virtual bool is_zero() const = 0; - virtual bool is_archived() const = 0; - virtual bool is_applied() const = 0; - virtual std::vector prev() const = 0; - virtual BlockIdExt one_prev(bool left) const = 0; - virtual std::vector next() const = 0; - virtual BlockIdExt one_next(bool left) const = 0; - virtual RootHash state() const = 0; - virtual td::uint32 version() const = 0; - - virtual bool processed() const = 0; - virtual void set_processed() = 0; - - virtual void flush(td::actor::ActorId manager, std::shared_ptr self, - td::Promise promise) = 0; - virtual void flushed_upto(td::uint32 version) = 0; - virtual void set_logical_time(LogicalTime lt) = 0; - virtual void set_unix_time(UnixTime ts) = 0; - virtual LogicalTime logical_time() const = 0; - virtual UnixTime unix_time() const = 0; - virtual void set_proof() = 0; - virtual void set_proof_link() = 0; - virtual void set_signatures() = 0; - virtual void set_next(BlockIdExt next) = 0; - virtual void set_prev(BlockIdExt prev) = 0; - virtual void set_received() = 0; - virtual void set_moved_to_storage() = 0; - virtual void set_deleted() = 0; - virtual void set_split(bool value) = 0; - virtual void set_merge(bool value) = 0; - virtual void set_is_key_block(bool value) = 0; - virtual void set_state_root_hash(RootHash hash) = 0; - virtual void set_state_boc() = 0; - virtual void set_deleted_state_boc() = 0; - virtual void set_archived() = 0; - virtual void set_applied() = 0; - - virtual td::BufferSlice serialize() const = 0; - - virtual ~BlockHandleImpl() = default; -}; - -using BlockHandle = std::shared_ptr; - -} // namespace ton diff --git a/ton-test-liteclient-full/lite-client/ton/ton-shard.h b/ton-test-liteclient-full/lite-client/ton/ton-shard.h index ada5cf8..289f632 100644 --- a/ton-test-liteclient-full/lite-client/ton/ton-shard.h +++ b/ton-test-liteclient-full/lite-client/ton/ton-shard.h @@ -28,6 +28,18 @@ inline AccountIdPrefixFull extract_addr_prefix(WorkchainId workchain, td::ConstB return AccountIdPrefixFull{workchain, extract_top64(addr)}; } +inline int count_matching_bits(AccountIdPrefix x, AccountIdPrefix y) { + return x == y ? 64 : td::count_leading_zeroes64(x ^ y); +} + +inline int count_matching_bits(AccountIdPrefixFull x, AccountIdPrefixFull y) { + if (x.workchain != y.workchain) { + return td::count_leading_zeroes32(x.workchain ^ y.workchain); + } else { + return 32 + count_matching_bits(x.account_id_prefix, y.account_id_prefix); + } +} + inline td::uint32 shard_prefix_length(ShardId shard) { return shard ? 63 - td::count_trailing_zeroes_non_zero64(shard) : 0; } @@ -63,7 +75,7 @@ inline bool shard_contains(ShardIdFull parent, ShardIdFull child) { return parent.workchain == child.workchain && shard_contains(parent.shard, child.shard); } -inline bool shard_contains(ShardIdFull parent, AccountIdPrefixFull child) { +inline bool shard_contains(ShardIdFull parent, const AccountIdPrefixFull& child) { return parent.workchain == child.workchain && shard_contains(parent.shard, child.account_id_prefix); } @@ -85,7 +97,7 @@ inline ShardId shard_intersection(ShardId x, ShardId y) { } inline ShardIdFull shard_intersection(ShardIdFull x, ShardIdFull y) { - return {x.workchain, shard_intersection(x. shard, y.shard)}; + return {x.workchain, shard_intersection(x.shard, y.shard)}; } inline bool is_right_child(ShardId x) { diff --git a/ton-test-liteclient-full/lite-client/ton/ton-types.h b/ton-test-liteclient-full/lite-client/ton/ton-types.h index 74a544d..8723842 100644 --- a/ton-test-liteclient-full/lite-client/ton/ton-types.h +++ b/ton-test-liteclient-full/lite-client/ton/ton-types.h @@ -29,8 +29,11 @@ using ValidatorSessionId = td::Bits256; constexpr WorkchainId masterchainId = -1, basechainId = 0, workchainInvalid = 0x80000000; constexpr ShardId shardIdAll = (1ULL << 63); -constexpr unsigned split_merge_delay = 100; // prepare (delay) split/merge for 100 seconds -constexpr unsigned split_merge_interval = 100; // split/merge is enabled during 60 second interval +constexpr unsigned split_merge_delay = 100; // prepare (delay) split/merge for 100 seconds +constexpr unsigned split_merge_interval = 100; // split/merge is enabled during 60 second interval +constexpr unsigned min_split_merge_interval = 30; // split/merge interval must be at least 30 seconds +constexpr unsigned max_split_merge_delay = + 1000; // end of split/merge interval must be at most 1000 seconds in the future static inline int shard_pfx_len(ShardId shard) { return shard ? 63 - td::count_trailing_zeroes_non_zero64(shard) : 0; @@ -384,4 +387,16 @@ struct Ed25519_PublicKey { } }; +struct ValidatorDescr { + /* ton::validator::ValidatorFullId */ Ed25519_PublicKey key; + ValidatorWeight weight; + /* adnl::AdnlNodeIdShort */ Bits256 addr; + ValidatorDescr(const Ed25519_PublicKey& key_, ValidatorWeight weight_) : key(key_), weight(weight_) { + addr.set_zero(); + } + ValidatorDescr(const Ed25519_PublicKey& key_, ValidatorWeight weight_, const Bits256& addr_) + : key(key_), weight(weight_), addr(addr_) { + } +}; + }; // namespace ton diff --git a/ton-test-liteclient-full/lite-client/validator/interfaces/config.h b/ton-test-liteclient-full/lite-client/validator/interfaces/config.h deleted file mode 100644 index 1d51244..0000000 --- a/ton-test-liteclient-full/lite-client/validator/interfaces/config.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "ton/ton-types.h" -#include "crypto/common/refcnt.hpp" - -namespace ton { - -namespace validator { - -class McShardHash : public td::CntObject { - public: - enum class FsmState { fsm_none, fsm_split, fsm_merge }; - virtual BlockIdExt top_block_id() const = 0; - virtual LogicalTime start_lt() const = 0; - virtual LogicalTime end_lt() const = 0; - virtual UnixTime fsm_utime() const = 0; - virtual FsmState fsm_state() const = 0; - virtual ShardIdFull shard() const = 0; - virtual bool before_split() const = 0; - virtual bool before_merge() const = 0; -}; - -} // namespace validator - -} // namespace ton diff --git a/ton-test-liteclient-full/lite-client/validator/interfaces/validator-full-id.h b/ton-test-liteclient-full/lite-client/validator/interfaces/validator-full-id.h deleted file mode 100644 index 27eda7e..0000000 --- a/ton-test-liteclient-full/lite-client/validator/interfaces/validator-full-id.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "ton/ton-types.h" -#include "auto/tl/ton_api.h" -#include "keys/keys.hpp" - -namespace ton { - -namespace validator { - -class ValidatorFullId : public PublicKey { - public: - NodeIdShort short_id() const; - operator Ed25519_PublicKey() const; - - ValidatorFullId(PublicKey id) : PublicKey{std::move(id)} { - } - ValidatorFullId(Ed25519_PublicKey key) : PublicKey{pubkeys::Ed25519{key.as_bits256()}} { - } -}; - -} // namespace validator - -} // namespace ton