From ce5880510450ee3cf133cb1501be8055f623ff0b Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Wed, 11 Dec 2024 14:48:48 +0300 Subject: [PATCH 01/38] Improve readability of validator-engine-console commands (#1426) 1. Add dashes to command names (old names still work for compatibility) 2. Better shard format 3. Allow base64 in some parameters --- ton/ton-types.h | 20 ++ .../validator-engine-console-query.cpp | 43 ++- .../validator-engine-console-query.h | 261 +++++++++--------- .../validator-engine-console.cpp | 10 +- .../validator-engine-console.h | 14 + 5 files changed, 193 insertions(+), 155 deletions(-) diff --git a/ton/ton-types.h b/ton/ton-types.h index cd9700814..2447a8c5d 100644 --- a/ton/ton-types.h +++ b/ton/ton-types.h @@ -120,6 +120,26 @@ struct ShardIdFull { char buffer[64]; return std::string{buffer, (unsigned)snprintf(buffer, 63, "(%d,%016llx)", workchain, (unsigned long long)shard)}; } + static td::Result parse(td::Slice s) { + // Formats: (0,2000000000000000) (0:2000000000000000) 0,2000000000000000 0:2000000000000000 + if (s.empty()) { + return td::Status::Error("empty string"); + } + if (s[0] == '(' && s.back() == ')') { + s = s.substr(1, s.size() - 2); + } + auto sep = s.find(':'); + if (sep == td::Slice::npos) { + sep = s.find(','); + } + if (sep == td::Slice::npos || s.size() - sep - 1 != 16) { + return td::Status::Error(PSTRING() << "invalid shard " << s); + } + ShardIdFull shard; + TRY_RESULT_ASSIGN(shard.workchain, td::to_integer_safe(s.substr(0, sep))); + TRY_RESULT_ASSIGN(shard.shard, td::hex_to_integer_safe(s.substr(sep + 1))); + return shard; + } }; struct AccountIdPrefixFull { diff --git a/validator-engine-console/validator-engine-console-query.cpp b/validator-engine-console/validator-engine-console-query.cpp index bfcd50da7..d11100194 100644 --- a/validator-engine-console/validator-engine-console-query.cpp +++ b/validator-engine-console/validator-engine-console-query.cpp @@ -1041,8 +1041,7 @@ td::Status ImportCertificateQuery::receive(td::BufferSlice data) { td::Status SignShardOverlayCertificateQuery::run() { - TRY_RESULT_ASSIGN(wc_, tokenizer_.get_token()); - TRY_RESULT_ASSIGN(shard_, tokenizer_.get_token() ); + TRY_RESULT_ASSIGN(shard_, tokenizer_.get_token() ); TRY_RESULT_ASSIGN(key_, tokenizer_.get_token()); TRY_RESULT_ASSIGN(expire_at_, tokenizer_.get_token()); TRY_RESULT_ASSIGN(max_size_, tokenizer_.get_token()); @@ -1052,8 +1051,9 @@ td::Status SignShardOverlayCertificateQuery::run() { } td::Status SignShardOverlayCertificateQuery::send() { - auto b = ton::create_serialize_tl_object - (wc_, shard_, ton::create_tl_object(key_.tl()), expire_at_, max_size_); + auto b = ton::create_serialize_tl_object( + shard_.workchain, shard_.shard, ton::create_tl_object(key_.tl()), + expire_at_, max_size_); td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); return td::Status::OK(); } @@ -1071,8 +1071,7 @@ td::Status SignShardOverlayCertificateQuery::receive(td::BufferSlice data) { } td::Status ImportShardOverlayCertificateQuery::run() { - TRY_RESULT_ASSIGN(wc_, tokenizer_.get_token()); - TRY_RESULT_ASSIGN(shard_, tokenizer_.get_token() ); + TRY_RESULT_ASSIGN(shard_, tokenizer_.get_token()); TRY_RESULT_ASSIGN(key_, tokenizer_.get_token()); TRY_RESULT_ASSIGN(in_file_, tokenizer_.get_token()); @@ -1083,8 +1082,9 @@ td::Status ImportShardOverlayCertificateQuery::send() { TRY_RESULT(data, td::read_file(in_file_)); TRY_RESULT_PREFIX(cert, ton::fetch_tl_object(data.as_slice(), true), "incorrect certificate"); - auto b = ton::create_serialize_tl_object - (wc_, shard_, ton::create_tl_object(key_.tl()), std::move(cert)); + auto b = ton::create_serialize_tl_object( + shard_.workchain, shard_.shard, ton::create_tl_object(key_.tl()), + std::move(cert)); td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); return td::Status::OK(); } @@ -1173,14 +1173,12 @@ td::Status GetPerfTimerStatsJsonQuery::receive(td::BufferSlice data) { } td::Status GetShardOutQueueSizeQuery::run() { - TRY_RESULT_ASSIGN(block_id_.workchain, tokenizer_.get_token()); - TRY_RESULT_ASSIGN(block_id_.shard, tokenizer_.get_token()); + TRY_RESULT(shard, tokenizer_.get_token()); + block_id_.workchain = shard.workchain; + block_id_.shard = shard.shard; TRY_RESULT_ASSIGN(block_id_.seqno, tokenizer_.get_token()); if (!tokenizer_.endl()) { - ton::ShardIdFull dest; - TRY_RESULT_ASSIGN(dest.workchain, tokenizer_.get_token()); - TRY_RESULT_ASSIGN(dest.shard, tokenizer_.get_token()); - dest_ = dest; + TRY_RESULT_ASSIGN(dest_, tokenizer_.get_token()); } TRY_STATUS(tokenizer_.check_endl()); return td::Status::OK(); @@ -1188,8 +1186,7 @@ td::Status GetShardOutQueueSizeQuery::run() { td::Status GetShardOutQueueSizeQuery::send() { auto b = ton::create_serialize_tl_object( - dest_ ? 1 : 0, ton::create_tl_block_id_simple(block_id_), dest_ ? dest_.value().workchain : 0, - dest_ ? dest_.value().shard : 0); + dest_.is_valid() ? 1 : 0, ton::create_tl_block_id_simple(block_id_), dest_.workchain, dest_.shard); td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); return td::Status::OK(); } @@ -1563,14 +1560,13 @@ td::Status GetAdnlStatsQuery::receive(td::BufferSlice data) { } td::Status AddShardQuery::run() { - TRY_RESULT_ASSIGN(wc_, tokenizer_.get_token()); - TRY_RESULT_ASSIGN(shard_, tokenizer_.get_token()); + TRY_RESULT_ASSIGN(shard_, tokenizer_.get_token()); + TRY_STATUS(tokenizer_.check_endl()); return td::Status::OK(); } td::Status AddShardQuery::send() { - auto b = ton::create_serialize_tl_object( - ton::create_tl_shard_id(ton::ShardIdFull(wc_, shard_))); + auto b = ton::create_serialize_tl_object(ton::create_tl_shard_id(shard_)); td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); return td::Status::OK(); } @@ -1583,14 +1579,13 @@ td::Status AddShardQuery::receive(td::BufferSlice data) { } td::Status DelShardQuery::run() { - TRY_RESULT_ASSIGN(wc_, tokenizer_.get_token()); - TRY_RESULT_ASSIGN(shard_, tokenizer_.get_token()); + TRY_RESULT_ASSIGN(shard_, tokenizer_.get_token()); + TRY_STATUS(tokenizer_.check_endl()); return td::Status::OK(); } td::Status DelShardQuery::send() { - auto b = ton::create_serialize_tl_object( - ton::create_tl_shard_id(ton::ShardIdFull(wc_, shard_))); + auto b = ton::create_serialize_tl_object(ton::create_tl_shard_id(shard_)); td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); return td::Status::OK(); } diff --git a/validator-engine-console/validator-engine-console-query.h b/validator-engine-console/validator-engine-console-query.h index f85179bde..817d70c9e 100644 --- a/validator-engine-console/validator-engine-console-query.h +++ b/validator-engine-console/validator-engine-console-query.h @@ -36,6 +36,7 @@ #include "ton/ton-types.h" #include "keys/keys.hpp" +#include "td/utils/base64.h" class ValidatorEngineConsole; @@ -95,27 +96,25 @@ inline td::Result Tokenizer::get_token() { } template <> -inline td::Result Tokenizer::get_token() { - TRY_RESULT(S, get_raw_token()); - TRY_RESULT(F, td::hex_decode(S)); - if (F.size() == 32) { - return ton::PublicKeyHash{td::Slice{F}}; +inline td::Result Tokenizer::get_token() { + TRY_RESULT(word, get_raw_token()); + std::string data; + if (word.size() == 64) { + TRY_RESULT_ASSIGN(data, td::hex_decode(word)); + } else if (word.size() == 44) { + TRY_RESULT_ASSIGN(data, td::base64_decode(word)); } else { return td::Status::Error("cannot parse keyhash: bad length"); } + td::Bits256 v; + v.as_slice().copy_from(data); + return v; } template <> -inline td::Result Tokenizer::get_token() { - TRY_RESULT(S, get_raw_token()); - TRY_RESULT(F, td::hex_decode(S)); - if (F.size() == 32) { - td::Bits256 v; - v.as_slice().copy_from(F); - return v; - } else { - return td::Status::Error("cannot parse keyhash: bad length"); - } +inline td::Result Tokenizer::get_token() { + TRY_RESULT(x, get_token()); + return ton::PublicKeyHash{x}; } template <> @@ -146,6 +145,18 @@ inline td::Result> Tokenizer::get_token_vector() { } } +template <> +inline td::Result Tokenizer::get_token() { + TRY_RESULT(word, get_raw_token()); + auto r_wc = td::to_integer_safe(word); + if (r_wc.is_ok()) { + TRY_RESULT_ASSIGN(word, get_raw_token()); + TRY_RESULT(shard, td::to_integer_safe(word)); + return ton::ShardIdFull{r_wc.move_as_ok(), shard}; + } + return ton::ShardIdFull::parse(word); +} + class QueryRunner { public: virtual ~QueryRunner() = default; @@ -222,10 +233,10 @@ class GetTimeQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice R) override; static std::string get_name() { - return "gettime"; + return "get-time"; } static std::string get_help() { - return "gettime\tshows current server unixtime"; + return "get-time\tshows current server unixtime"; } std::string name() const override { return get_name(); @@ -287,10 +298,10 @@ class NewKeyQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice R) override; static std::string get_name() { - return "newkey"; + return "new-key"; } static std::string get_help() { - return "newkey\tgenerates new key pair on server"; + return "new-key\tgenerates new key pair on server"; } std::string name() const override { return get_name(); @@ -308,10 +319,10 @@ class ImportPrivateKeyFileQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice R) override; static std::string get_name() { - return "importf"; + return "import-f"; } static std::string get_help() { - return "importf \timport private key"; + return "import-f \timport private key"; } std::string name() const override { return get_name(); @@ -330,10 +341,10 @@ class ExportPublicKeyQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice R) override; static std::string get_name() { - return "exportpub"; + return "export-pub"; } static std::string get_help() { - return "exportpub \texports public key by key hash"; + return "export-pub \texports public key by key hash"; } std::string name() const override { return get_name(); @@ -352,10 +363,10 @@ class ExportPublicKeyFileQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice R) override; static std::string get_name() { - return "exportpubf"; + return "export-pubf"; } static std::string get_help() { - return "exportpubf \texports public key by key hash"; + return "export-pub-f \texports public key by key hash"; } std::string name() const override { return get_name(); @@ -398,10 +409,10 @@ class SignFileQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "signf"; + return "sign-f"; } static std::string get_help() { - return "signf \tsigns bytestring with privkey"; + return "sign-f \tsigns bytestring with privkey"; } std::string name() const override { return get_name(); @@ -422,10 +433,10 @@ class ExportAllPrivateKeysQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice R) override; static std::string get_name() { - return "exportallprivatekeys"; + return "export-all-private-keys"; } static std::string get_help() { - return "exportallprivatekeys \texports all private keys from validator engine and stores them to " + return "export-all-private-keys \texports all private keys from validator engine and stores them to " ""; } std::string name() const override { @@ -446,10 +457,10 @@ class AddAdnlAddrQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "addadnl"; + return "add-adnl"; } static std::string get_help() { - return "addadnl \tuse key as ADNL addr"; + return "add-adnl \tuse key as ADNL addr"; } std::string name() const override { return get_name(); @@ -469,10 +480,10 @@ class AddDhtIdQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "adddht"; + return "add-dht"; } static std::string get_help() { - return "adddht \tcreate DHT node with specified ADNL addr"; + return "add-dht \tcreate DHT node with specified ADNL addr"; } std::string name() const override { return get_name(); @@ -491,10 +502,10 @@ class AddValidatorPermanentKeyQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "addpermkey"; + return "add-perm-key"; } static std::string get_help() { - return "addpermkey \tadd validator permanent key"; + return "add-perm-key \tadd validator permanent key"; } std::string name() const override { return get_name(); @@ -515,10 +526,10 @@ class AddValidatorTempKeyQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "addtempkey"; + return "add-temp-key"; } static std::string get_help() { - return "addtempkey \tadd validator temp key"; + return "add-temp-key \tadd validator temp key"; } std::string name() const override { return get_name(); @@ -539,10 +550,10 @@ class AddValidatorAdnlAddrQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "addvalidatoraddr"; + return "add-validator-addr"; } static std::string get_help() { - return "addvalidatoraddr \tadd validator ADNL addr"; + return "add-validator-addr \tadd validator ADNL addr"; } std::string name() const override { return get_name(); @@ -563,10 +574,10 @@ class ChangeFullNodeAdnlAddrQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "changefullnodeaddr"; + return "change-full-node-addr"; } static std::string get_help() { - return "changefullnodeaddr \tchanges fullnode ADNL address"; + return "change-full-node-addr \tchanges fullnode ADNL address"; } std::string name() const override { return get_name(); @@ -585,10 +596,10 @@ class AddLiteServerQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "addliteserver"; + return "add-liteserver"; } static std::string get_help() { - return "addliteserver \tadd liteserver"; + return "add-liteserver \tadd liteserver"; } std::string name() const override { return get_name(); @@ -608,10 +619,10 @@ class DelAdnlAddrQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "deladnl"; + return "del-adnl"; } static std::string get_help() { - return "deladnl \tdel unused ADNL addr"; + return "del-adnl \tdel unused ADNL addr"; } std::string name() const override { return get_name(); @@ -630,10 +641,10 @@ class DelDhtIdQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "deldht"; + return "del-dht"; } static std::string get_help() { - return "deldht \tdel unused DHT node"; + return "del-dht \tdel unused DHT node"; } std::string name() const override { return get_name(); @@ -652,10 +663,10 @@ class DelValidatorPermanentKeyQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "delpermkey"; + return "del-perm-key"; } static std::string get_help() { - return "delpermkey \tforce del unused validator permanent key"; + return "del-perm-key \tforce del unused validator permanent key"; } std::string name() const override { return get_name(); @@ -674,10 +685,10 @@ class DelValidatorTempKeyQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "deltempkey"; + return "del-temp-key"; } static std::string get_help() { - return "deltempkey \tforce del unused validator temp key"; + return "del-temp-key \tforce del unused validator temp key"; } std::string name() const override { return get_name(); @@ -697,10 +708,10 @@ class DelValidatorAdnlAddrQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "delvalidatoraddr"; + return "del-validator-addr"; } static std::string get_help() { - return "delvalidatoraddr \tforce del unused validator ADNL addr"; + return "del-validator-addr \tforce del unused validator ADNL addr"; } std::string name() const override { return get_name(); @@ -720,10 +731,10 @@ class GetConfigQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "getconfig"; + return "get-config"; } static std::string get_help() { - return "getconfig\tdownloads current config"; + return "get-config\tdownloads current config"; } std::string name() const override { return get_name(); @@ -741,10 +752,10 @@ class SetVerbosityQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "setverbosity"; + return "set-verbosity"; } static std::string get_help() { - return "setverbosity \tchanges verbosity level"; + return "set-verbosity \tchanges verbosity level"; } std::string name() const override { return get_name(); @@ -763,10 +774,10 @@ class GetStatsQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "getstats"; + return "get-stats"; } static std::string get_help() { - return "getstats\tprints stats"; + return "get-stats\tprints stats"; } std::string name() const override { return get_name(); @@ -807,10 +818,10 @@ class AddNetworkAddressQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "addaddr"; + return "add-addr"; } static std::string get_help() { - return "addaddr {cats...} {priocats...}\tadds ip address to address list"; + return "add-addr {cats...} {priocats...}\tadds ip address to address list"; } std::string name() const override { return get_name(); @@ -831,10 +842,10 @@ class AddNetworkProxyAddressQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "addproxyaddr"; + return "add-proxy-addr"; } static std::string get_help() { - return "addproxyaddr {cats...} {priocats...}\tadds ip address to address list"; + return "add-proxy-addr {cats...} {priocats...}\tadds ip address to address list"; } std::string name() const override { return get_name(); @@ -858,10 +869,10 @@ class CreateElectionBidQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "createelectionbid"; + return "create-election-bid"; } static std::string get_help() { - return "createelectionbid \tcreate election bid"; + return "create-election-bid \tcreate election bid"; } std::string name() const override { return get_name(); @@ -883,10 +894,10 @@ class CreateProposalVoteQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "createproposalvote"; + return "create-proposal-vote"; } static std::string get_help() { - return "createproposalvote \tcreate proposal vote"; + return "create-proposal-vote \tcreate proposal vote"; } std::string name() const override { return get_name(); @@ -906,10 +917,10 @@ class CreateComplaintVoteQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "createcomplaintvote"; + return "create-complaint-vote"; } static std::string get_help() { - return "createcomplaintvote \tcreate proposal vote"; + return "create-complaint-vote \tcreate proposal vote"; } std::string name() const override { return get_name(); @@ -930,10 +941,10 @@ class CheckDhtServersQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "checkdht"; + return "check-dht"; } static std::string get_help() { - return "checkdht \tchecks, which root DHT servers are accessible from this ADNL addr"; + return "check-dht \tchecks, which root DHT servers are accessible from this ADNL addr"; } std::string name() const override { return get_name(); @@ -952,10 +963,10 @@ class GetOverlaysStatsQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "getoverlaysstats"; + return "get-overlays-stats"; } static std::string get_help() { - return "getoverlaysstats\tgets stats for all overlays"; + return "get-overlays-stats\tgets stats for all overlays"; } std::string name() const override { return get_name(); @@ -971,10 +982,10 @@ class GetOverlaysStatsJsonQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "getoverlaysstatsjson"; + return "get-overlays-stats-json"; } static std::string get_help() { - return "getoverlaysstatsjson \tgets stats for all overlays and writes to json file"; + return "get-overlays-stats-json \tgets stats for all overlays and writes to json file"; } std::string name() const override { return get_name(); @@ -993,10 +1004,11 @@ class SignCertificateQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "signcert"; + return "sign-cert"; } static std::string get_help() { - return "signcert \tsign overlay certificate by key"; + return "sign-cert \tsign overlay certificate by " + " key"; } std::string name() const override { return get_name(); @@ -1029,10 +1041,10 @@ class ImportCertificateQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "importcert"; + return "import-cert"; } static std::string get_help() { - return "importcert \timport overlay certificate for specific key"; + return "import-cert \timport overlay certificate for specific key"; } std::string name() const override { return get_name(); @@ -1054,10 +1066,11 @@ class SignShardOverlayCertificateQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "signshardoverlaycert"; + return "sign-shard-overlay-cert"; } static std::string get_help() { - return "signshardoverlaycert \tsign certificate for in currently active shard overlay"; + return "sign-shard-overlay-cert : \tsign certificate " + "for in currently active shard overlay"; } std::string name() const override { return get_name(); @@ -1065,8 +1078,7 @@ class SignShardOverlayCertificateQuery : public Query { private: - td::int32 wc_; - td::int64 shard_; + ton::ShardIdFull shard_; td::int32 expire_at_; ton::PublicKeyHash key_; td::uint32 max_size_; @@ -1083,10 +1095,11 @@ class ImportShardOverlayCertificateQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "importshardoverlaycert"; + return "import-shard-overlay-cert"; } static std::string get_help() { - return "importshardoverlaycert \timport certificate for in currently active shard overlay"; + return "import-shard-overlay-cert : \timport certificate for in " + "currently active shard overlay"; } std::string name() const override { return get_name(); @@ -1094,8 +1107,7 @@ class ImportShardOverlayCertificateQuery : public Query { private: - td::int32 wc_; - td::int64 shard_; + ton::ShardIdFull shard_; ton::PublicKeyHash key_; std::string in_file_; }; @@ -1109,10 +1121,10 @@ class GetActorStatsQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "getactorstats"; + return "get-actor-stats"; } static std::string get_help() { - return "getactorstats []\tget actor stats and print it either in stdout or in "; + return "get-actor-stats []\tget actor stats and print it either in stdout or in "; } std::string name() const override { return get_name(); @@ -1131,10 +1143,11 @@ class GetPerfTimerStatsJsonQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "getperftimerstatsjson"; + return "get-perf-timer-stats-json"; } static std::string get_help() { - return "getperftimerstatsjson \tgets min, average and max event processing time for last 60, 300 and 3600 seconds and writes to json file"; + return "get-perf-timer-stats-json \tgets min, average and max event processing time for last 60, 300 and " + "3600 seconds and writes to json file"; } std::string name() const override { return get_name(); @@ -1153,10 +1166,10 @@ class GetShardOutQueueSizeQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "getshardoutqueuesize"; + return "get-shard-out-queue-size"; } static std::string get_help() { - return "getshardoutqueuesize [ ]\treturns number of messages in the " + return "get-shard-out-queue-size : [:]\treturns number of messages in the " "queue of the given shard. Destination shard is optional."; } std::string name() const override { @@ -1165,7 +1178,7 @@ class GetShardOutQueueSizeQuery : public Query { private: ton::BlockId block_id_; - td::optional dest_; + ton::ShardIdFull dest_ = ton::ShardIdFull{ton::workchainInvalid}; }; class SetExtMessagesBroadcastDisabledQuery : public Query { @@ -1177,11 +1190,11 @@ class SetExtMessagesBroadcastDisabledQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "setextmessagesbroadcastdisabled"; + return "set-ext-messages-broadcast-disabled"; } static std::string get_help() { - return "setextmessagesbroadcastdisabled \tdisable broadcasting and rebroadcasting ext messages; value is 0 " - "or 1."; + return "set-ext-messages-broadcast-disabled \tdisable broadcasting and rebroadcasting ext messages; value " + "is 0 or 1."; } std::string name() const override { return get_name(); @@ -1200,10 +1213,10 @@ class AddCustomOverlayQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "addcustomoverlay"; + return "add-custom-overlay"; } static std::string get_help() { - return "addcustomoverlay \tadd custom overlay with config from file "; + return "add-custom-overlay \tadd custom overlay with config from file "; } std::string name() const override { return get_name(); @@ -1222,10 +1235,10 @@ class DelCustomOverlayQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "delcustomoverlay"; + return "del-custom-overlay"; } static std::string get_help() { - return "delcustomoverlay \tdelete custom overlay with name "; + return "del-custom-overlay \tdelete custom overlay with name "; } std::string name() const override { return get_name(); @@ -1244,10 +1257,10 @@ class ShowCustomOverlaysQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "showcustomoverlays"; + return "show-custom-overlays"; } static std::string get_help() { - return "showcustomoverlays\tshow all custom overlays"; + return "show-custom-overlays\tshow all custom overlays"; } std::string name() const override { return get_name(); @@ -1263,10 +1276,10 @@ class SetStateSerializerEnabledQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "setstateserializerenabled"; + return "set-state-serializer-enabled"; } static std::string get_help() { - return "setstateserializerenabled \tdisable or enable persistent state serializer; value is 0 or 1"; + return "set-state-serializer-enabled \tdisable or enable persistent state serializer; value is 0 or 1"; } std::string name() const override { return get_name(); @@ -1285,10 +1298,10 @@ class SetCollatorOptionsJsonQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "setcollatoroptionsjson"; + return "set-collator-options-json"; } static std::string get_help() { - return "setcollatoroptionsjson \tset collator options from file "; + return "set-collator-options-json \tset collator options from file "; } std::string name() const override { return get_name(); @@ -1307,10 +1320,10 @@ class ResetCollatorOptionsQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "resetcollatoroptions"; + return "reset-collator-options"; } static std::string get_help() { - return "resetcollatoroptions\tset collator options to default values"; + return "reset-collator-options\tset collator options to default values"; } std::string name() const override { return get_name(); @@ -1326,10 +1339,10 @@ class GetCollatorOptionsJsonQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "getcollatoroptionsjson"; + return "get-collator-options-json"; } static std::string get_help() { - return "getcollatoroptionsjson \tsave current collator options to file "; + return "get-collator-options-json \tsave current collator options to file "; } std::string name() const override { return get_name(); @@ -1348,11 +1361,11 @@ class GetAdnlStatsJsonQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "getadnlstatsjson"; + return "get-adnl-stats-json"; } static std::string get_help() { - return "getadnlstatsjson [all]\tsave adnl stats to . all - returns all peers (default - only " - "peers with traffic in the last 10 minutes)"; + return "get-adnl-stats-json [all]\tsave adnl stats to . all - returns all peers (default - " + "only peers with traffic in the last 10 minutes)"; } std::string name() const override { return get_name(); @@ -1372,11 +1385,11 @@ class GetAdnlStatsQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "getadnlstats"; + return "get-adnl-stats"; } static std::string get_help() { - return "getadnlstats [all]\tdisplay adnl stats. all - returns all peers (default - only peers with traffic in the " - "last 10 minutes)"; + return "get-adnl-stats [all]\tdisplay adnl stats. all - returns all peers (default - only peers with traffic in " + "the last 10 minutes)"; } std::string name() const override { return get_name(); @@ -1396,18 +1409,17 @@ class AddShardQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "addshard"; + return "add-shard"; } static std::string get_help() { - return "addshard \tstart monitoring shard"; + return "add-shard :\tstart monitoring shard"; } std::string name() const override { return get_name(); } private: - td::int32 wc_; - td::int64 shard_; + ton::ShardIdFull shard_; }; class DelShardQuery : public Query { @@ -1419,16 +1431,15 @@ class DelShardQuery : public Query { td::Status send() override; td::Status receive(td::BufferSlice data) override; static std::string get_name() { - return "delshard"; + return "del-shard"; } static std::string get_help() { - return "delshard \tstop monitoring shard"; + return "del-shard :\tstop monitoring shard"; } std::string name() const override { return get_name(); } private: - td::int32 wc_; - td::int64 shard_; + ton::ShardIdFull shard_; }; \ No newline at end of file diff --git a/validator-engine-console/validator-engine-console.cpp b/validator-engine-console/validator-engine-console.cpp index 85c92564b..234cd6a5f 100644 --- a/validator-engine-console/validator-engine-console.cpp +++ b/validator-engine-console/validator-engine-console.cpp @@ -206,9 +206,8 @@ void ValidatorEngineConsole::show_help(std::string command, td::Promisehelp() << "\n"; } } else { - auto it = query_runners_.find(command); - if (it != query_runners_.end()) { - td::TerminalIO::out() << it->second->help() << "\n"; + if (auto query = get_query(command)) { + td::TerminalIO::out() << query->help() << "\n"; } else { td::TerminalIO::out() << "unknown command '" << command << "'\n"; } @@ -232,10 +231,9 @@ void ValidatorEngineConsole::parse_line(td::BufferSlice data) { } auto name = tokenizer.get_token().move_as_ok(); - auto it = query_runners_.find(name); - if (it != query_runners_.end()) { + if (auto query = get_query(name)) { running_queries_++; - it->second->run(actor_id(this), std::move(tokenizer)); + query->run(actor_id(this), std::move(tokenizer)); } else { td::TerminalIO::out() << "unknown command '" << name << "'\n"; } diff --git a/validator-engine-console/validator-engine-console.h b/validator-engine-console/validator-engine-console.h index 7a3842769..c802794ef 100644 --- a/validator-engine-console/validator-engine-console.h +++ b/validator-engine-console/validator-engine-console.h @@ -57,9 +57,23 @@ class ValidatorEngineConsole : public td::actor::Actor { std::unique_ptr make_callback(); std::map> query_runners_; + std::map alternate_names_; + static std::string simplify_name(std::string name) { + std::erase_if(name, [](char c) { return c == '-'; }); + return name; + } void add_query_runner(std::unique_ptr runner) { auto name = runner->name(); query_runners_[name] = std::move(runner); + alternate_names_[simplify_name(name)] = name; + } + QueryRunner* get_query(std::string name) { + auto it = alternate_names_.find(name); + if (it != alternate_names_.end()) { + name = it->second; + } + auto it2 = query_runners_.find(name); + return it2 == query_runners_.end() ? nullptr : it2->second.get(); } public: From f03f6ce7cad7c367f9ad93157ee63a7eb6483820 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Tue, 17 Dec 2024 11:16:43 +0300 Subject: [PATCH 02/38] Fix check_underflow in some instructions --- crypto/vm/dictops.cpp | 4 ++-- crypto/vm/tonops.cpp | 10 +++++++++- doc/GlobalVersions.md | 5 ++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/crypto/vm/dictops.cpp b/crypto/vm/dictops.cpp index d0ea8daa8..02f26fdd2 100644 --- a/crypto/vm/dictops.cpp +++ b/crypto/vm/dictops.cpp @@ -566,7 +566,7 @@ int exec_dict_getnear(VmState* st, unsigned args) { int exec_pfx_dict_set(VmState* st, Dictionary::SetMode mode, const char* name) { Stack& stack = st->get_stack(); VM_LOG(st) << "execute PFXDICT" << name; - stack.check_underflow(3); + stack.check_underflow(st->get_global_version() >= 9 ? 4 : 3); int n = stack.pop_smallint_range(PrefixDictionary::max_key_bits); PrefixDictionary dict{stack.pop_maybe_cell(), n}; auto key_slice = stack.pop_cellslice(); @@ -580,7 +580,7 @@ int exec_pfx_dict_set(VmState* st, Dictionary::SetMode mode, const char* name) { int exec_pfx_dict_delete(VmState* st) { Stack& stack = st->get_stack(); VM_LOG(st) << "execute PFXDICTDEL\n"; - stack.check_underflow(2); + stack.check_underflow(st->get_global_version() >= 9 ? 3 : 2); int n = stack.pop_smallint_range(PrefixDictionary::max_key_bits); PrefixDictionary dict{stack.pop_maybe_cell(), n}; auto key_slice = stack.pop_cellslice(); diff --git a/crypto/vm/tonops.cpp b/crypto/vm/tonops.cpp index 6c698df4b..d134f80b7 100644 --- a/crypto/vm/tonops.cpp +++ b/crypto/vm/tonops.cpp @@ -279,6 +279,7 @@ int exec_get_global_id(VmState* st) { int exec_get_gas_fee(VmState* st) { VM_LOG(st) << "execute GETGASFEE"; Stack& stack = st->get_stack(); + stack.check_underflow(st->get_global_version() >= 9 ? 2 : 0); bool is_masterchain = stack.pop_bool(); td::uint64 gas = stack.pop_long_range(std::numeric_limits::max(), 0); block::GasLimitsPrices prices = util::get_gas_prices(get_unpacked_config_tuple(st), is_masterchain); @@ -289,6 +290,7 @@ int exec_get_gas_fee(VmState* st) { int exec_get_storage_fee(VmState* st) { VM_LOG(st) << "execute GETSTORAGEFEE"; Stack& stack = st->get_stack(); + stack.check_underflow(st->get_global_version() >= 9 ? 4 : 0); bool is_masterchain = stack.pop_bool(); td::int64 delta = stack.pop_long_range(std::numeric_limits::max(), 0); td::uint64 bits = stack.pop_long_range(std::numeric_limits::max(), 0); @@ -302,6 +304,7 @@ int exec_get_storage_fee(VmState* st) { int exec_get_forward_fee(VmState* st) { VM_LOG(st) << "execute GETFORWARDFEE"; Stack& stack = st->get_stack(); + stack.check_underflow(st->get_global_version() >= 9 ? 3 : 0); bool is_masterchain = stack.pop_bool(); td::uint64 bits = stack.pop_long_range(std::numeric_limits::max(), 0); td::uint64 cells = stack.pop_long_range(std::numeric_limits::max(), 0); @@ -320,6 +323,7 @@ int exec_get_precompiled_gas(VmState* st) { int exec_get_original_fwd_fee(VmState* st) { VM_LOG(st) << "execute GETORIGINALFWDFEE"; Stack& stack = st->get_stack(); + stack.check_underflow(st->get_global_version() >= 9 ? 2 : 0); bool is_masterchain = stack.pop_bool(); td::RefInt256 fwd_fee = stack.pop_int_finite(); if (fwd_fee->sgn() < 0) { @@ -333,6 +337,7 @@ int exec_get_original_fwd_fee(VmState* st) { int exec_get_gas_fee_simple(VmState* st) { VM_LOG(st) << "execute GETGASFEESIMPLE"; Stack& stack = st->get_stack(); + stack.check_underflow(st->get_global_version() >= 9 ? 2 : 0); bool is_masterchain = stack.pop_bool(); td::uint64 gas = stack.pop_long_range(std::numeric_limits::max(), 0); block::GasLimitsPrices prices = util::get_gas_prices(get_unpacked_config_tuple(st), is_masterchain); @@ -343,6 +348,7 @@ int exec_get_gas_fee_simple(VmState* st) { int exec_get_forward_fee_simple(VmState* st) { VM_LOG(st) << "execute GETFORWARDFEESIMPLE"; Stack& stack = st->get_stack(); + stack.check_underflow(st->get_global_version() >= 9 ? 3 : 0); bool is_masterchain = stack.pop_bool(); td::uint64 bits = stack.pop_long_range(std::numeric_limits::max(), 0); td::uint64 cells = stack.pop_long_range(std::numeric_limits::max(), 0); @@ -373,6 +379,7 @@ void register_ton_config_ops(OpcodeTable& cp0) { .insert(OpcodeInstr::mksimple(0xf833, 16, "CONFIGOPTPARAM", std::bind(exec_get_config_param, _1, true))) .insert(OpcodeInstr::mksimple(0xf83400, 24, "PREVMCBLOCKS", std::bind(exec_get_prev_blocks_info, _1, 0, "PREVMCBLOCKS"))->require_version(4)) .insert(OpcodeInstr::mksimple(0xf83401, 24, "PREVKEYBLOCK", std::bind(exec_get_prev_blocks_info, _1, 1, "PREVKEYBLOCK"))->require_version(4)) + .insert(OpcodeInstr::mksimple(0xf83402, 24, "PREVMCBLOCKS_100", std::bind(exec_get_prev_blocks_info, _1, 2, "PREVMCBLOCKS_100"))->require_version(9)) .insert(OpcodeInstr::mksimple(0xf835, 16, "GLOBALID", exec_get_global_id)->require_version(4)) .insert(OpcodeInstr::mksimple(0xf836, 16, "GETGASFEE", exec_get_gas_fee)->require_version(6)) .insert(OpcodeInstr::mksimple(0xf837, 16, "GETSTORAGEFEE", exec_get_storage_fee)->require_version(6)) @@ -538,9 +545,10 @@ int exec_hash_ext(VmState* st, unsigned args) { VM_LOG(st) << "execute HASHEXT" << (append ? "A" : "") << (rev ? "R" : "") << " " << (hash_id == 255 ? -1 : hash_id); Stack& stack = st->get_stack(); if (hash_id == 255) { + stack.check_underflow(st->get_global_version() >= 9 ? 2 : 0); hash_id = stack.pop_smallint_range(254); } - int cnt = stack.pop_smallint_range(stack.depth() - 1); + int cnt = stack.pop_smallint_range(stack.depth() - 1 - (st->get_global_version() >= 9 ? (int)append : 0)); Hasher hasher{hash_id}; size_t total_bits = 0; long long gas_consumed = 0; diff --git a/doc/GlobalVersions.md b/doc/GlobalVersions.md index 5db1ab768..11b9b68c8 100644 --- a/doc/GlobalVersions.md +++ b/doc/GlobalVersions.md @@ -3,6 +3,7 @@ Global version is a parameter specified in `ConfigParam 8` ([block.tlb](https:// Various features are enabled depending on the global version. ## Version 4 +New features of version 4 are desctibed in detail in [the documentation](https://docs.ton.org/v3/documentation/tvm/changelog/tvm-upgrade-2023-07). ### New TVM instructions * `PREVMCBLOCKS`, `PREVKEYBLOCK` @@ -122,4 +123,6 @@ Operations for working with Merkle proofs, where cells can have non-zero level a ### Other changes - Fix `RAWRESERVE` action with flag `4` (use original balance of the account) by explicitly setting `original_balance` to `balance - msg_balance_remaining`. - Previously it did not work if storage fee was greater than the original balance. -- Jumps to nested continuations of depth more than 8 consume 1 gas for eact subsequent continuation (this does not affect most of TVM code). \ No newline at end of file +- Jumps to nested continuations of depth more than 8 consume 1 gas for eact subsequent continuation (this does not affect most of TVM code). +- Fix exception code in some TVM instructions: now `stk_und` has priority over other error codes. + - `PFXDICTADD`, `PFXDICTSET`, `PFXDICTREPLACE`, `PFXDICTDEL`, `GETGASFEE`, `GETSTORAGEFEE`, `GETFORWARDFEE`, `GETORIGINALFWDFEE`, `GETGASFEESIMPLE`, `GETFORWARDFEESIMPLE`, `HASHEXT` \ No newline at end of file From a01c7e2e75b04f00f41bc1eeec8f7855fed7194e Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Tue, 17 Dec 2024 11:17:54 +0300 Subject: [PATCH 03/38] Add more recent blocks to "previous blocks info" --- crypto/block/mc-config.cpp | 34 +++++++++++++++++++++++++++------- crypto/block/transaction.cpp | 3 ++- doc/GlobalVersions.md | 5 +++++ 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/crypto/block/mc-config.cpp b/crypto/block/mc-config.cpp index 56ee85ae3..148819136 100644 --- a/crypto/block/mc-config.cpp +++ b/crypto/block/mc-config.cpp @@ -2292,7 +2292,8 @@ Ref ConfigInfo::lookup_library(td::ConstBitPtr root_hash) const { td::Result> ConfigInfo::get_prev_blocks_info() const { // [ wc:Integer shard:Integer seqno:Integer root_hash:Integer file_hash:Integer] = BlockId; // [ last_mc_blocks:[BlockId...] - // prev_key_block:BlockId ] : PrevBlocksInfo + // prev_key_block:BlockId + // last_mc_blocks_100[BlockId...] ] : PrevBlocksInfo auto block_id_to_tuple = [](const ton::BlockIdExt& block_id) -> vm::Ref { td::RefInt256 shard = td::make_refint(block_id.id.shard); if (shard->sgn() < 0) { @@ -2302,25 +2303,44 @@ td::Result> ConfigInfo::get_prev_blocks_info() const { td::make_refint(block_id.id.seqno), td::bits_to_refint(block_id.root_hash.bits(), 256), td::bits_to_refint(block_id.file_hash.bits(), 256)); }; - std::vector last_mc_blocks; + std::vector tuple; + std::vector last_mc_blocks; last_mc_blocks.push_back(block_id_to_tuple(block_id)); for (ton::BlockSeqno seqno = block_id.id.seqno; seqno > 0 && last_mc_blocks.size() < 16;) { --seqno; - ton::BlockIdExt block_id; - if (!get_old_mc_block_id(seqno, block_id)) { + ton::BlockIdExt id; + if (!get_old_mc_block_id(seqno, id)) { return td::Status::Error("cannot fetch old mc block"); } - last_mc_blocks.push_back(block_id_to_tuple(block_id)); + last_mc_blocks.push_back(block_id_to_tuple(id)); } + tuple.push_back(td::make_cnt_ref>(std::move(last_mc_blocks))); ton::BlockIdExt last_key_block; ton::LogicalTime last_key_block_lt; if (!get_last_key_block(last_key_block, last_key_block_lt)) { return td::Status::Error("cannot fetch last key block"); } - return vm::make_tuple_ref(td::make_cnt_ref>(std::move(last_mc_blocks)), - block_id_to_tuple(last_key_block)); + tuple.push_back(block_id_to_tuple(last_key_block)); + + if (get_global_version() >= 9) { + std::vector last_mc_blocks_100; + for (ton::BlockSeqno seqno = block_id.id.seqno / 100 * 100; last_mc_blocks_100.size() < 16;) { + ton::BlockIdExt id; + if (!get_old_mc_block_id(seqno, id)) { + return td::Status::Error("cannot fetch old mc block"); + } + last_mc_blocks_100.push_back(block_id_to_tuple(id)); + if (seqno < 100) { + break; + } + seqno -= 100; + } + tuple.push_back(td::make_cnt_ref>(std::move(last_mc_blocks_100))); + } + + return td::make_cnt_ref>(std::move(tuple)); } td::optional PrecompiledContractsConfig::get_contract( diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index a32bad52f..7acd18348 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -1336,7 +1336,8 @@ Ref Transaction::prepare_vm_c7(const ComputePhaseConfig& cfg) const { // See crypto/block/mc-config.cpp#2223 (get_prev_blocks_info) // [ wc:Integer shard:Integer seqno:Integer root_hash:Integer file_hash:Integer] = BlockId; // [ last_mc_blocks:[BlockId...] - // prev_key_block:BlockId ] : PrevBlocksInfo + // prev_key_block:BlockId + // last_mc_blocks_100:[BlockId...] ] : PrevBlocksInfo // The only context where PrevBlocksInfo (13 parameter of c7) is null is inside emulator // where it need to be set via transaction_emulator_set_prev_blocks_info (see emulator/emulator-extern.cpp) // Inside validator, collator and liteserver checking external message contexts diff --git a/doc/GlobalVersions.md b/doc/GlobalVersions.md index 11b9b68c8..6b31e3eeb 100644 --- a/doc/GlobalVersions.md +++ b/doc/GlobalVersions.md @@ -114,11 +114,16 @@ Operations for working with Merkle proofs, where cells can have non-zero level a ## Version 9 +### c7 tuple +c7 tuple parameter number **13** (previous blocks info tuple) now has the third element. It contains ids of the 16 last masterchain blocks with seqno divisible by 100. +Example: if the last masterchain block seqno is `19071` then the list contains block ids with seqnos `19000`, `18900`, ..., `17500`. + ### New TVM instructions - `SECP256K1_XONLY_PUBKEY_TWEAK_ADD` (`key tweak - 0 or f x y -1`) - performs [`secp256k1_xonly_pubkey_tweak_add`](https://github.com/bitcoin-core/secp256k1/blob/master/include/secp256k1_extrakeys.h#L120). `key` and `tweak` are 256-bit unsigned integers. 65-byte public key is returned as `uint8 f`, `uint256 x, y` (as in `ECRECOVER`). Gas cost: `1276`. - `mask SETCONTCTRMANY` (`cont - cont'`) - takes continuation, performs the equivalent of `c[i] PUSHCTR SWAP c[i] SETCONTCNR` for each `i` that is set in `mask` (mask is in `0..255`). - `SETCONTCTRMANYX` (`cont mask - cont'`) - same as `SETCONTCTRMANY`, but takes `mask` from stack. +- `PREVMCBLOCKS_100` returns the third element of the previous block info tuple (see above). ### Other changes - Fix `RAWRESERVE` action with flag `4` (use original balance of the account) by explicitly setting `original_balance` to `balance - msg_balance_remaining`. From 0fff1bd8c78e89aee179192e40f98545e825b28b Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Tue, 17 Dec 2024 11:18:34 +0300 Subject: [PATCH 04/38] Fix loading library cell in contract code --- crypto/block/transaction.cpp | 3 +- crypto/fift/lib/Asm.fif | 1 + crypto/smc-envelope/SmartContract.cpp | 4 +- crypto/vm/contops.cpp | 6 +-- crypto/vm/vm.cpp | 54 +++++++++++---------------- crypto/vm/vm.h | 18 ++++----- doc/GlobalVersions.md | 3 +- lite-client/lite-client.cpp | 2 +- utils/opcode-timing.cpp | 4 +- validator/impl/liteserver.cpp | 10 ++++- 10 files changed, 49 insertions(+), 56 deletions(-) diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index 7acd18348..920433761 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -1692,9 +1692,8 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) { } } } - vm::VmState vm{new_code, std::move(stack), gas, 1, new_data, vm_log, compute_vm_libraries(cfg)}; + vm::VmState vm{new_code, cfg.global_version, std::move(stack), gas, 1, new_data, vm_log, compute_vm_libraries(cfg)}; vm.set_max_data_depth(cfg.max_vm_data_depth); - vm.set_global_version(cfg.global_version); vm.set_c7(prepare_vm_c7(cfg)); // tuple with SmartContractInfo vm.set_chksig_always_succeed(cfg.ignore_chksig); vm.set_stop_on_accept_message(cfg.stop_on_accept_message); diff --git a/crypto/fift/lib/Asm.fif b/crypto/fift/lib/Asm.fif index 39cb759d1..976093f80 100644 --- a/crypto/fift/lib/Asm.fif +++ b/crypto/fift/lib/Asm.fif @@ -1312,6 +1312,7 @@ x{F832} @Defop CONFIGPARAM x{F833} @Defop CONFIGOPTPARAM x{F83400} @Defop PREVMCBLOCKS x{F83401} @Defop PREVKEYBLOCK +x{F83402} @Defop PREVMCBLOCKS_100 x{F835} @Defop GLOBALID x{F836} @Defop GETGASFEE x{F837} @Defop GETSTORAGEFEE diff --git a/crypto/smc-envelope/SmartContract.cpp b/crypto/smc-envelope/SmartContract.cpp index 2578a9514..c8e438ec7 100644 --- a/crypto/smc-envelope/SmartContract.cpp +++ b/crypto/smc-envelope/SmartContract.cpp @@ -222,14 +222,14 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Refdump(os, 2); LOG(DEBUG) << "VM stack:\n" << os.str(); } - vm::VmState vm{state.code, std::move(stack), gas, 1, state.data, log}; + int global_version = config ? config->get_global_version() : 0; + vm::VmState vm{state.code, global_version, std::move(stack), gas, 1, state.data, log}; vm.set_c7(std::move(c7)); vm.set_chksig_always_succeed(ignore_chksig); if (!libraries.is_null()) { vm.register_library_collection(libraries); } if (config) { - vm.set_global_version(config->get_global_version()); auto r_limits = config->get_size_limits_config(); if (r_limits.is_ok()) { vm.set_max_data_depth(r_limits.ok().max_vm_data_depth); diff --git a/crypto/vm/contops.cpp b/crypto/vm/contops.cpp index 3b8926586..1ccf53daf 100644 --- a/crypto/vm/contops.cpp +++ b/crypto/vm/contops.cpp @@ -261,10 +261,10 @@ int exec_runvm_common(VmState* st, unsigned mode) { vm::GasLimits gas{gas_limit, gas_max}; VmStateInterface::Guard guard{nullptr}; // Don't consume gas for creating/loading cells during VM init - VmState new_state{std::move(code), std::move(new_stack), gas, (int)mode & 3, std::move(data), - VmLog{}, std::vector>{}, std::move(c7)}; + VmState new_state{ + std::move(code), st->get_global_version(), std::move(new_stack), gas, (int)mode & 3, std::move(data), + VmLog{}, std::vector>{}, std::move(c7)}; new_state.set_chksig_always_succeed(st->get_chksig_always_succeed()); - new_state.set_global_version(st->get_global_version()); st->run_child_vm(std::move(new_state), with_data, mode & 32, mode & 8, mode & 128, ret_vals); return 0; } diff --git a/crypto/vm/vm.cpp b/crypto/vm/vm.cpp index fb774f80a..77d5d8f80 100644 --- a/crypto/vm/vm.cpp +++ b/crypto/vm/vm.cpp @@ -22,6 +22,8 @@ #include "vm/log.h" #include "vm/vm.h" #include "cp0.h" +#include "memo.h" + #include namespace vm { @@ -31,33 +33,8 @@ VmState::VmState() : cp(-1), dispatch(&dummy_dispatch_table), quit0(true, 0), qu init_cregs(); } -VmState::VmState(Ref _code) - : code(std::move(_code)), cp(-1), dispatch(&dummy_dispatch_table), quit0(true, 0), quit1(true, 1) { - ensure_throw(init_cp(0)); - init_cregs(); -} - -VmState::VmState(Ref _code, Ref _stack, int flags, Ref _data, VmLog log, - std::vector> _libraries, Ref init_c7) - : code(std::move(_code)) - , stack(std::move(_stack)) - , cp(-1) - , dispatch(&dummy_dispatch_table) - , quit0(true, 0) - , quit1(true, 1) - , log(log) - , libraries(std::move(_libraries)) - , stack_trace((flags >> 2) & 1) { - ensure_throw(init_cp(0)); - set_c4(std::move(_data)); - if (init_c7.not_null()) { - set_c7(std::move(init_c7)); - } - init_cregs(flags & 1, flags & 2); -} - -VmState::VmState(Ref _code, Ref _stack, const GasLimits& gas, int flags, Ref _data, VmLog log, - std::vector> _libraries, Ref init_c7) +VmState::VmState(Ref _code, int global_version, Ref _stack, const GasLimits& gas, int flags, + Ref _data, VmLog log, std::vector> _libraries, Ref init_c7) : code(std::move(_code)) , stack(std::move(_stack)) , cp(-1) @@ -67,7 +44,8 @@ VmState::VmState(Ref _code, Ref _stack, const GasLimits& gas, , log(log) , gas(gas) , libraries(std::move(_libraries)) - , stack_trace((flags >> 2) & 1) { + , stack_trace((flags >> 2) & 1) + , global_version(global_version) { ensure_throw(init_cp(0)); set_c4(std::move(_data)); if (init_c7.not_null()) { @@ -102,12 +80,24 @@ void VmState::init_cregs(bool same_c3, bool push_0) { } } -Ref VmState::convert_code_cell(Ref code_cell) { +Ref VmState::convert_code_cell(Ref code_cell, int global_version, + const std::vector>& libraries) { if (code_cell.is_null()) { return {}; } - Ref csr{true, NoVmOrd(), code_cell}; - if (csr->is_valid()) { + Ref csr; + if (global_version >= 9) { + // Use DummyVmState instead of this to avoid consuming gas for cell loading + DummyVmState dummy{libraries, global_version}; + Guard guard(&dummy); + try { + csr = load_cell_slice_ref(code_cell); + } catch (VmError&) { // NOLINT(*-empty-catch) + } + } else { + csr = td::Ref{true, NoVmOrd(), code_cell}; + } + if (csr.not_null() && csr->is_valid()) { return csr; } return load_cell_slice_ref(CellBuilder{}.store_ref(std::move(code_cell)).finalize()); @@ -577,6 +567,7 @@ int run_vm_code(Ref code, Ref& stack, int flags, Ref* da GasLimits* gas_limits, std::vector> libraries, Ref init_c7, Ref* actions_ptr, int global_version) { VmState vm{code, + global_version, std::move(stack), gas_limits ? *gas_limits : GasLimits{}, flags, @@ -584,7 +575,6 @@ int run_vm_code(Ref code, Ref& stack, int flags, Ref* da log, std::move(libraries), std::move(init_c7)}; - vm.set_global_version(global_version); int res = vm.run(); stack = vm.get_stack_ref(); if (vm.committed() && data_ptr) { diff --git a/crypto/vm/vm.h b/crypto/vm/vm.h index cf5322938..04c5e576c 100644 --- a/crypto/vm/vm.h +++ b/crypto/vm/vm.h @@ -164,14 +164,12 @@ class VmState final : public VmStateInterface { bls_pairing_element_gas_price = 11800 }; VmState(); - VmState(Ref _code); - VmState(Ref _code, Ref _stack, int flags = 0, Ref _data = {}, VmLog log = {}, - std::vector> _libraries = {}, Ref init_c7 = {}); - VmState(Ref _code, Ref _stack, const GasLimits& _gas, int flags = 0, Ref _data = {}, + VmState(Ref _code, int global_version, Ref _stack, const GasLimits& _gas, int flags = 0, Ref _data = {}, VmLog log = {}, std::vector> _libraries = {}, Ref init_c7 = {}); - template - VmState(Ref code_cell, Args&&... args) - : VmState(convert_code_cell(std::move(code_cell)), std::forward(args)...) { + VmState(Ref _code, int global_version, Ref _stack, const GasLimits& _gas, int flags = 0, + Ref _data = {}, VmLog log = {}, std::vector> _libraries = {}, Ref init_c7 = {}) + : VmState(convert_code_cell(std::move(_code), global_version, _libraries), global_version, std::move(_stack), + _gas, flags, std::move(_data), std::move(log), _libraries, std::move(init_c7)) { } VmState(const VmState&) = delete; VmState(VmState&&) = default; @@ -345,9 +343,6 @@ class VmState final : public VmStateInterface { int get_global_version() const override { return global_version; } - void set_global_version(int version) { - global_version = version; - } int call(Ref cont); int call(Ref cont, int pass_args, int ret_args = -1); int jump(Ref cont); @@ -382,7 +377,8 @@ class VmState final : public VmStateInterface { } return res; } - static Ref convert_code_cell(Ref code_cell); + static Ref convert_code_cell(Ref code_cell, int global_version, + const std::vector>& libraries); bool try_commit(); void force_commit(); diff --git a/doc/GlobalVersions.md b/doc/GlobalVersions.md index 6b31e3eeb..64b2342a4 100644 --- a/doc/GlobalVersions.md +++ b/doc/GlobalVersions.md @@ -130,4 +130,5 @@ Example: if the last masterchain block seqno is `19071` then the list contains b - Previously it did not work if storage fee was greater than the original balance. - Jumps to nested continuations of depth more than 8 consume 1 gas for eact subsequent continuation (this does not affect most of TVM code). - Fix exception code in some TVM instructions: now `stk_und` has priority over other error codes. - - `PFXDICTADD`, `PFXDICTSET`, `PFXDICTREPLACE`, `PFXDICTDEL`, `GETGASFEE`, `GETSTORAGEFEE`, `GETFORWARDFEE`, `GETORIGINALFWDFEE`, `GETGASFEESIMPLE`, `GETFORWARDFEESIMPLE`, `HASHEXT` \ No newline at end of file + - `PFXDICTADD`, `PFXDICTSET`, `PFXDICTREPLACE`, `PFXDICTDEL`, `GETGASFEE`, `GETSTORAGEFEE`, `GETFORWARDFEE`, `GETORIGINALFWDFEE`, `GETGASFEESIMPLE`, `GETFORWARDFEESIMPLE`, `HASHEXT` +- Now setting the contract code to a library cell does not consume additional gas on execution of the code. \ No newline at end of file diff --git a/lite-client/lite-client.cpp b/lite-client/lite-client.cpp index dc09ae52b..1050e6d27 100644 --- a/lite-client/lite-client.cpp +++ b/lite-client/lite-client.cpp @@ -2227,7 +2227,7 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt // auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr); vm::GasLimits gas{gas_limit}; LOG(DEBUG) << "creating VM"; - vm::VmState vm{code, std::move(stack), gas, 1, data, vm::VmLog()}; + vm::VmState vm{code, ton::SUPPORTED_VERSION, std::move(stack), gas, 1, data, vm::VmLog()}; vm.set_c7(liteclient::prepare_vm_c7(info.gen_utime, info.gen_lt, td::make_ref(acc.addr->clone()), balance)); // tuple with SmartContractInfo // vm.incr_stack_trace(1); // enable stack dump after each step diff --git a/utils/opcode-timing.cpp b/utils/opcode-timing.cpp index 876ba109e..47171eec0 100644 --- a/utils/opcode-timing.cpp +++ b/utils/opcode-timing.cpp @@ -135,8 +135,8 @@ runInfo time_run_vm(td::Slice command, td::Ref stack) { CHECK(stack.is_unique()); try { vm::GasLimits gas_limit; - vm::VmState vm{vm::load_cell_slice_ref(cell), std::move(stack), gas_limit, 0, {}, vm::VmLog{}, {}, c7}; - vm.set_global_version(ton::SUPPORTED_VERSION); + vm::VmState vm{ + vm::load_cell_slice_ref(cell), ton::SUPPORTED_VERSION, std::move(stack), gas_limit, 0, {}, vm::VmLog{}, {}, c7}; std::clock_t cStart = std::clock(); int ret = ~vm.run(); std::clock_t cEnd = std::clock(); diff --git a/validator/impl/liteserver.cpp b/validator/impl/liteserver.cpp index 6bd4e4219..723dbfe97 100644 --- a/validator/impl/liteserver.cpp +++ b/validator/impl/liteserver.cpp @@ -1520,11 +1520,17 @@ void LiteQuery::finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice libraries.push_back(acc_libs); } vm::GasLimits gas{gas_limit, gas_limit}; - vm::VmState vm{code, std::move(stack_), gas, 1, std::move(data), vm::VmLog::Null(), std::move(libraries)}; + vm::VmState vm{code, + config->get_global_version(), + std::move(stack_), + gas, + 1, + std::move(data), + vm::VmLog::Null(), + std::move(libraries)}; auto c7 = prepare_vm_c7(gen_utime, gen_lt, td::make_ref(acc.addr->clone()), balance, config.get(), std::move(code), due_payment); vm.set_c7(c7); // tuple with SmartContractInfo - vm.set_global_version(config->get_global_version()); // vm.incr_stack_trace(1); // enable stack dump after each step LOG(INFO) << "starting VM to run GET-method of smart contract " << acc_workchain_ << ":" << acc_addr_.to_hex(); // **** RUN VM **** From 46d4e12b4c78d5d4f37850e2f7910ad4b06d51fe Mon Sep 17 00:00:00 2001 From: neodix42 Date: Tue, 7 Jan 2025 08:15:51 -0800 Subject: [PATCH 05/38] extend generate-random-id utility... (#1462) * improve windows builds * install nasm for openssl compilation on win * install nasm for openssl compilation on win for github * add create-state, proxy-liteserver, rldp-http-proxy, http-proxy, adnl-proxy, dht-server, libtonlibjson.so and libemulator.so to docker image * build new artifacts inside Docker * add files smartcont/auto/* to docker image * build arm64 in docker branch build * improve secp256k1 build * extend generate-random-id with -f parameter (to read addr list from a file) --- utils/generate-random-id.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/utils/generate-random-id.cpp b/utils/generate-random-id.cpp index f606f3581..a487ac172 100644 --- a/utils/generate-random-id.cpp +++ b/utils/generate-random-id.cpp @@ -84,6 +84,19 @@ int main(int argc, char *argv[]) { TRY_RESULT_PREFIX_ASSIGN(addr_list, ton::adnl::AdnlAddressList::create(addr_list_tl), "bad addr list: "); return td::Status::OK(); }); + p.add_checked_option('f', "path to file with addr-list", "addr list to sign", [&](td::Slice key) { + if (addr_list) { + return td::Status::Error("duplicate '-f' option"); + } + + td::BufferSlice bs(key); + TRY_RESULT_PREFIX(data, td::read_file(key.str()), "failed to read addr-list: "); + TRY_RESULT_PREFIX(as_json_value, td::json_decode(data.as_slice()), "bad addr list JSON: "); + ton::tl_object_ptr addr_list_tl; + TRY_STATUS_PREFIX(td::from_json(addr_list_tl, std::move(as_json_value)), "bad addr list TL: "); + TRY_RESULT_PREFIX_ASSIGN(addr_list, ton::adnl::AdnlAddressList::create(addr_list_tl), "bad addr list: "); + return td::Status::OK(); + }); p.add_checked_option('i', "network-id", "dht network id (default: -1)", [&](td::Slice key) { if (network_id_opt) { return td::Status::Error("duplicate '-i' option"); From dc2f0dad818bea29d9a5c2549a6c3861e8c60129 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Mon, 13 Jan 2025 17:39:56 +0300 Subject: [PATCH 06/38] Add extra currencies to c7 in tonlib runGetMethod --- crypto/smc-envelope/SmartContract.cpp | 21 +++++++++++---------- crypto/smc-envelope/SmartContract.h | 5 +++++ tonlib/tonlib/TonlibClient.cpp | 21 ++++++++++++--------- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/crypto/smc-envelope/SmartContract.cpp b/crypto/smc-envelope/SmartContract.cpp index 2578a9514..8ec2c1469 100644 --- a/crypto/smc-envelope/SmartContract.cpp +++ b/crypto/smc-envelope/SmartContract.cpp @@ -149,16 +149,17 @@ td::Ref prepare_vm_c7(SmartContract::Args args, td::Ref cod } std::vector tuple = { - td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea - td::make_refint(0), // actions:Integer - td::make_refint(0), // msgs_sent:Integer - td::make_refint(now), // unixtime:Integer - td::make_refint(0), //TODO: // block_lt:Integer - td::make_refint(0), //TODO: // trans_lt:Integer - std::move(rand_seed_int), // rand_seed:Integer - block::CurrencyCollection(args.balance).as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)] - vm::load_cell_slice_ref(address), // myself:MsgAddressInt - vm::StackEntry::maybe(config) // vm::StackEntry::maybe(td::Ref()) + td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea + td::make_refint(0), // actions:Integer + td::make_refint(0), // msgs_sent:Integer + td::make_refint(now), // unixtime:Integer + td::make_refint(0), // block_lt:Integer (TODO) + td::make_refint(0), // trans_lt:Integer (TODO) + std::move(rand_seed_int), // rand_seed:Integer + block::CurrencyCollection(args.balance, args.extra_currencies) + .as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)] + vm::load_cell_slice_ref(address), // myself:MsgAddressInt + vm::StackEntry::maybe(config) // vm::StackEntry::maybe(td::Ref()) }; if (args.config && args.config.value()->get_global_version() >= 4) { tuple.push_back(vm::StackEntry::maybe(code)); // code:Cell diff --git a/crypto/smc-envelope/SmartContract.h b/crypto/smc-envelope/SmartContract.h index 7fc93579d..49edb9693 100644 --- a/crypto/smc-envelope/SmartContract.h +++ b/crypto/smc-envelope/SmartContract.h @@ -64,6 +64,7 @@ class SmartContract : public td::CntObject { bool ignore_chksig{false}; td::uint64 amount{0}; td::uint64 balance{0}; + td::Ref extra_currencies; int vm_log_verbosity_level{0}; bool debug_enabled{false}; @@ -121,6 +122,10 @@ class SmartContract : public td::CntObject { this->balance = balance; return std::move(*this); } + Args&& set_extra_currencies(td::Ref extra_currencies) { + this->extra_currencies = std::move(extra_currencies); + return std::move(*this); + } Args&& set_address(block::StdAddress address) { this->address = address; return std::move(*this); diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index 507512d0f..d917a57a8 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -1050,15 +1050,17 @@ class Query { } vm::GasLimits gas_limits = compute_gas_limits(td::make_refint(raw_.source->get_balance()), gas_limits_prices); - auto res = smc.write().send_external_message(raw_.message_body, ton::SmartContract::Args() - .set_limits(gas_limits) - .set_balance(raw_.source->get_balance()) - .set_now(raw_.source->get_sync_time()) - .set_ignore_chksig(ignore_chksig) - .set_address(raw_.source->get_address()) - .set_config(cfg) - .set_prev_blocks_info(state.prev_blocks_info) - .set_libraries(libraries)); + auto res = smc.write().send_external_message(raw_.message_body, + ton::SmartContract::Args() + .set_limits(gas_limits) + .set_balance(raw_.source->get_balance()) + .set_extra_currencies(raw_.source->get_extra_currencies()) + .set_now(raw_.source->get_sync_time()) + .set_ignore_chksig(ignore_chksig) + .set_address(raw_.source->get_address()) + .set_config(cfg) + .set_prev_blocks_info(state.prev_blocks_info) + .set_libraries(libraries)); td::int64 fwd_fee = 0; if (res.success) { LOG(DEBUG) << "output actions:\n" @@ -4790,6 +4792,7 @@ td::Status TonlibClient::do_request(const tonlib_api::smc_runGetMethod& request, } args.set_stack(std::move(stack)); args.set_balance(it->second->get_balance()); + args.set_extra_currencies(it->second->get_extra_currencies()); args.set_now(it->second->get_sync_time()); args.set_address(it->second->get_address()); From 4ddb14c136d18a8d462c70f775115e925f701b5b Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Mon, 13 Jan 2025 17:40:16 +0300 Subject: [PATCH 07/38] Fix double tilde for crc computation in tlbc --- crypto/tl/tlbc.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/crypto/tl/tlbc.cpp b/crypto/tl/tlbc.cpp index 0050e1610..d3a6edb5c 100644 --- a/crypto/tl/tlbc.cpp +++ b/crypto/tl/tlbc.cpp @@ -1800,9 +1800,6 @@ void Constructor::show(std::ostream& os, int mode) const { } for (int i = 0; i < type_arity; i++) { os << ' '; - if (param_negated.at(i)) { - os << '~'; - } params.at(i)->show(os, this, 100, mode | 1); } if (!(mode & 2)) { From cae9ccfacf594c44852f320330ef512b3f0c859b Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Mon, 13 Jan 2025 17:41:10 +0300 Subject: [PATCH 08/38] Retry dht query in adnl-peer if peer does not respond for too long --- adnl/adnl-peer.cpp | 14 ++++++++++++++ adnl/adnl-peer.hpp | 1 + 2 files changed, 15 insertions(+) diff --git a/adnl/adnl-peer.cpp b/adnl/adnl-peer.cpp index 7f5c60394..ab4600581 100644 --- a/adnl/adnl-peer.cpp +++ b/adnl/adnl-peer.cpp @@ -119,6 +119,7 @@ void AdnlPeerPairImpl::discover() { void AdnlPeerPairImpl::receive_packet_checked(AdnlPacket packet) { last_received_packet_ = td::Timestamp::now(); try_reinit_at_ = td::Timestamp::never(); + drop_addr_list_at_ = td::Timestamp::never(); request_reverse_ping_after_ = td::Timestamp::in(15.0); auto d = Adnl::adnl_start_time(); if (packet.dst_reinit_date() > d) { @@ -415,6 +416,9 @@ void AdnlPeerPairImpl::send_packet_continue(AdnlPacket packet, td::actor::ActorI if (!try_reinit_at_ && last_received_packet_ < td::Timestamp::in(-5.0)) { try_reinit_at_ = td::Timestamp::in(10.0); } + if (!drop_addr_list_at_ && last_received_packet_ < td::Timestamp::in(-60.0 * 9.0)) { + drop_addr_list_at_ = td::Timestamp::in(60.0); + } packet.run_basic_checks().ensure(); auto B = serialize_tl_object(packet.tl(), true); if (via_channel) { @@ -692,6 +696,16 @@ void AdnlPeerPairImpl::reinit(td::int32 date) { } td::Result, bool>> AdnlPeerPairImpl::get_conn() { + if (drop_addr_list_at_ && drop_addr_list_at_.is_in_past()) { + drop_addr_list_at_ = td::Timestamp::never(); + priority_addr_list_ = AdnlAddressList{}; + priority_conns_.clear(); + addr_list_ = AdnlAddressList{}; + conns_.clear(); + has_reverse_addr_ = false; + return td::Status::Error(ErrorCode::notready, "no active connections"); + } + if (!priority_addr_list_.empty() && priority_addr_list_.expire_at() < td::Clocks::system()) { priority_addr_list_ = AdnlAddressList{}; priority_conns_.clear(); diff --git a/adnl/adnl-peer.hpp b/adnl/adnl-peer.hpp index 7db2e2a1d..243974ba1 100644 --- a/adnl/adnl-peer.hpp +++ b/adnl/adnl-peer.hpp @@ -266,6 +266,7 @@ class AdnlPeerPairImpl : public AdnlPeerPair { td::Timestamp last_received_packet_ = td::Timestamp::never(); td::Timestamp try_reinit_at_ = td::Timestamp::never(); + td::Timestamp drop_addr_list_at_ = td::Timestamp::never(); bool has_reverse_addr_ = false; td::Timestamp request_reverse_ping_after_ = td::Timestamp::now(); From 87c4b4a5d4d5c6a12f12d446aff8b40d36223245 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Mon, 13 Jan 2025 17:41:50 +0300 Subject: [PATCH 09/38] Fix handling small out-of-sync in validate-query --- validator/downloaders/wait-block-state.cpp | 5 +- validator/impl/validate-query.cpp | 59 ++++++++++++---------- validator/impl/validate-query.hpp | 1 + validator/manager.cpp | 2 +- 4 files changed, 37 insertions(+), 30 deletions(-) diff --git a/validator/downloaders/wait-block-state.cpp b/validator/downloaders/wait-block-state.cpp index b61b94922..c80e7d896 100644 --- a/validator/downloaders/wait-block-state.cpp +++ b/validator/downloaders/wait-block-state.cpp @@ -67,7 +67,8 @@ void WaitBlockState::start() { if (reading_from_db_) { return; } - if (handle_->received_state()) { + bool inited_proof = handle_->id().is_masterchain() ? handle_->inited_proof() : handle_->inited_proof_link(); + if (handle_->received_state() && inited_proof) { reading_from_db_ = true; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { @@ -107,7 +108,7 @@ void WaitBlockState::start() { }); td::actor::send_closure(manager_, &ValidatorManager::send_get_zero_state_request, handle_->id(), priority_, std::move(P)); - } else if (check_persistent_state_desc()) { + } else if (check_persistent_state_desc() && !handle_->received_state()) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { if (R.is_error()) { LOG(WARNING) << "failed to get persistent state: " << R.move_as_error(); diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index 8490567e8..9a66ea81f 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -346,16 +346,7 @@ void ValidateQuery::start_up() { // return; } } - // 2. learn latest masterchain state and block id - LOG(DEBUG) << "sending get_top_masterchain_state_block() to Manager"; - ++pending; - td::actor::send_closure_later(manager, &ValidatorManager::get_top_masterchain_state_block, - [self = get_self()](td::Result, BlockIdExt>> res) { - LOG(DEBUG) << "got answer to get_top_masterchain_state_block"; - td::actor::send_closure_later( - std::move(self), &ValidateQuery::after_get_latest_mc_state, std::move(res)); - }); - // 3. load state(s) corresponding to previous block(s) + // 2. load state(s) corresponding to previous block(s) prev_states.resize(prev_blocks.size()); for (int i = 0; (unsigned)i < prev_blocks.size(); i++) { // 3.1. load state @@ -368,21 +359,13 @@ void ValidateQuery::start_up() { std::move(self), &ValidateQuery::after_get_shard_state, i, std::move(res)); }); } - // 4. unpack block candidate (while necessary data is being loaded) + // 3. unpack block candidate (while necessary data is being loaded) if (!unpack_block_candidate()) { reject_query("error unpacking block candidate"); return; } - // 5. request masterchain state referred to in the block + // 4. request masterchain handle and state referred to in the block if (!is_masterchain()) { - ++pending; - td::actor::send_closure_later(manager, &ValidatorManager::wait_block_state_short, mc_blkid_, priority(), timeout, - [self = get_self()](td::Result> res) { - LOG(DEBUG) << "got answer to wait_block_state() query for masterchain block"; - td::actor::send_closure_later(std::move(self), &ValidateQuery::after_get_mc_state, - std::move(res)); - }); - // 5.1. request corresponding block handle ++pending; td::actor::send_closure_later(manager, &ValidatorManager::get_block_handle, mc_blkid_, true, [self = get_self()](td::Result res) { @@ -663,6 +646,19 @@ bool ValidateQuery::extract_collated_data() { return true; } +/** + * Send get_top_masterchain_state_block to manager, call after_get_latest_mc_state afterwards + */ +void ValidateQuery::request_latest_mc_state() { + ++pending; + td::actor::send_closure_later(manager, &ValidatorManager::get_top_masterchain_state_block, + [self = get_self()](td::Result, BlockIdExt>> res) { + LOG(DEBUG) << "got answer to get_top_masterchain_state_block"; + td::actor::send_closure_later( + std::move(self), &ValidateQuery::after_get_latest_mc_state, std::move(res)); + }); +} + /** * Callback function called after retrieving the latest masterchain state. * @@ -710,6 +706,7 @@ void ValidateQuery::after_get_latest_mc_state(td::Result> res) { + CHECK(!is_masterchain()); LOG(WARNING) << "in ValidateQuery::after_get_mc_state() for " << mc_blkid_.to_str(); --pending; if (res.is_error()) { @@ -720,6 +717,7 @@ void ValidateQuery::after_get_mc_state(td::Result> res) { fatal_error("cannot process masterchain state for "s + mc_blkid_.to_str()); return; } + request_latest_mc_state(); if (!pending) { if (!try_validate()) { fatal_error("cannot validate new block"); @@ -734,17 +732,21 @@ void ValidateQuery::after_get_mc_state(td::Result> res) { */ void ValidateQuery::got_mc_handle(td::Result res) { LOG(DEBUG) << "in ValidateQuery::got_mc_handle() for " << mc_blkid_.to_str(); - --pending; if (res.is_error()) { fatal_error(res.move_as_error()); return; } - auto handle = res.move_as_ok(); - if (!handle->inited_proof() && mc_blkid_.seqno()) { - fatal_error(-666, "reference masterchain block "s + mc_blkid_.to_str() + " for block " + id_.to_str() + - " does not have a valid proof"); - return; - } + auto mc_handle = res.move_as_ok(); + td::actor::send_closure_later( + manager, &ValidatorManager::wait_block_state, mc_handle, priority(), timeout, + [self = get_self(), id = id_, mc_handle](td::Result> res) { + LOG(DEBUG) << "got answer to wait_block_state() query for masterchain block"; + if (res.is_ok() && mc_handle->id().seqno() > 0 && !mc_handle->inited_proof()) { + res = td::Status::Error(-666, "reference masterchain block "s + mc_handle->id().to_str() + " for block " + + id.to_str() + " does not have a valid proof"); + } + td::actor::send_closure_later(std::move(self), &ValidateQuery::after_get_mc_state, std::move(res)); + }); } /** @@ -778,6 +780,9 @@ void ValidateQuery::after_get_shard_state(int idx, td::Result> r return; } } + if (is_masterchain()) { + request_latest_mc_state(); + } if (!pending) { if (!try_validate()) { fatal_error("cannot validate new block"); diff --git a/validator/impl/validate-query.hpp b/validator/impl/validate-query.hpp index 52d4968ad..98cd2493b 100644 --- a/validator/impl/validate-query.hpp +++ b/validator/impl/validate-query.hpp @@ -284,6 +284,7 @@ class ValidateQuery : public td::actor::Actor { return actor_id(this); } + void request_latest_mc_state(); void after_get_latest_mc_state(td::Result, BlockIdExt>> res); void after_get_mc_state(td::Result> res); void got_mc_handle(td::Result res); diff --git a/validator/manager.cpp b/validator/manager.cpp index a631bd093..068ea5eb1 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -1343,7 +1343,7 @@ void ValidatorManagerImpl::written_handle(BlockHandle handle, td::Promisesecond.actor_, &WaitBlockData::force_read_from_db); } } - if (inited_state) { + if (inited_state && inited_proof) { auto it = wait_state_.find(handle->id()); if (it != wait_state_.end()) { td::actor::send_closure(it->second.actor_, &WaitBlockState::force_read_from_db); From 652f4f01411913227cd554cfe88374ae81e8fe95 Mon Sep 17 00:00:00 2001 From: crStiv Date: Wed, 15 Jan 2025 08:36:46 +0100 Subject: [PATCH 10/38] Update Changelog.md (#1476) Co-authored-by: EmelyanenkoK --- Changelog.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 15e2450d9..fd513bc81 100644 --- a/Changelog.md +++ b/Changelog.md @@ -31,7 +31,7 @@ Besides the work of the core team, this update is based on the efforts of @krigg ## 2024.08 Update 1. Introduction of dispatch queues, message envelopes with transaction chain metadata, and explicitly stored msg_queue size, which will be activated by `Config8.version >= 8` and new `Config8.capabilities` bits: `capStoreOutMsgQueueSize`, `capMsgMetadata`, `capDeferMessages`. -2. A number of changes to transcation executor which will activated for `Config8.version >= 8`: +2. A number of changes to transaction executor which will activated for `Config8.version >= 8`: - Check mode on invalid `action_send_msg`. Ignore action if `IGNORE_ERROR` (+2) bit is set, bounce if `BOUNCE_ON_FAIL` (+16) bit is set. - Slightly change random seed generation to fix mix of `addr_rewrite` and `addr`. - Fill in `skipped_actions` for both invalid and valid messages with `IGNORE_ERROR` mode that can't be sent. @@ -103,7 +103,7 @@ Besides the work of the core team, this update is based on the efforts of @akifo * Fix error in proof generation for blocks after merge * Fix most of `block is not applied` issues related to sending too recent block in Proofs * LS now check external messages till `accept_message` (`set_gas`). -3. Improvements in DHT work and storage, CellDb, config.json ammendment, peer misbehavior detection, validator session stats collection, emulator. +3. Improvements in DHT work and storage, CellDb, config.json amendment, peer misbehavior detection, validator session stats collection, emulator. 4. Change in CTOS and XLOAD behavior activated through setting `version >= 5` in `ConfigParam 8;`: * Loading "nested libraries" (i.e. a library cell that points to another library cell) throws an exception. * Loading a library consumes gas for cell load only once (for the library cell), not twice (both for the library cell and the cell in the library). @@ -114,7 +114,7 @@ Besides the work of the Core team, this update is based on the efforts of @XaBbl ## 2023.12 Update 1. Optimized message queue handling, now queue cleaning speed doesn't depend on total queue size - * Cleaning delivered messages using lt augmentation instead of random search / consequtive walk + * Cleaning delivered messages using lt augmentation instead of random search / consecutive walk * Keeping root cell of queue message in memory until outdated (caching) 2. Changes to block collation/validation limits 3. Stop accepting new external message if message queue is overloaded @@ -206,7 +206,7 @@ Besides the work of the core team, this update is based on the efforts of @vtama Besides the work of the core team, this update is based on the efforts of @tvorogme (debug improvements), @AlexeyFSL (WASM builds) and third-party security auditors. ## 2022.08 Update -* Blockchain state serialization now works via separate db-handler which simplfies memory clearing after serialization +* Blockchain state serialization now works via separate db-handler which simplifies memory clearing after serialization * CellDB now works asynchronously which substantially increase database access throughput * Abseil-cpp and crc32 updated: solve issues with compilation on recent OS distributives * Fixed a series of UBs and issues for exotic endianness hosts From f6fa986b3326888a27a1161725c3034a7e558862 Mon Sep 17 00:00:00 2001 From: "Victor S." <53380262+1IxI1@users.noreply.github.com> Date: Wed, 15 Jan 2025 10:39:05 +0300 Subject: [PATCH 11/38] Fix *DATASIZE* opcode log msg (#1465) Co-authored-by: EmelyanenkoK --- crypto/vm/tonops.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/vm/tonops.cpp b/crypto/vm/tonops.cpp index 6c698df4b..6eebbc6de 100644 --- a/crypto/vm/tonops.cpp +++ b/crypto/vm/tonops.cpp @@ -1293,7 +1293,7 @@ void register_ton_crypto_ops(OpcodeTable& cp0) { } int exec_compute_data_size(VmState* st, int mode) { - VM_LOG(st) << (mode & 2 ? 'S' : 'C') << "DATASIZE" << (mode & 1 ? "Q" : ""); + VM_LOG(st) << "execute " << (mode & 2 ? 'S' : 'C') << "DATASIZE" << (mode & 1 ? "Q" : ""); Stack& stack = st->get_stack(); stack.check_underflow(2); auto bound = stack.pop_int(); From 62838571ebbb874985ec78146eee9c37868a2c03 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Wed, 15 Jan 2025 07:43:33 +0000 Subject: [PATCH 12/38] Support extra currencies in reserve action with +2 flag (#1429) * Support extra currencies in reserve action with +2 flag * Enable new reserve behavior in version 9 --- crypto/block/block.cpp | 30 ++++++++++++++++++++++++++++++ crypto/block/block.h | 1 + crypto/block/transaction.cpp | 22 +++++++++++++--------- crypto/block/transaction.h | 1 + doc/GlobalVersions.md | 3 ++- validator/impl/validate-query.cpp | 1 + 6 files changed, 48 insertions(+), 10 deletions(-) diff --git a/crypto/block/block.cpp b/crypto/block/block.cpp index 98546a2d4..302a2aa46 100644 --- a/crypto/block/block.cpp +++ b/crypto/block/block.cpp @@ -1319,6 +1319,36 @@ CurrencyCollection CurrencyCollection::operator-(td::RefInt256 other_grams) cons } } +bool CurrencyCollection::clamp(const CurrencyCollection& other) { + if (!is_valid() || !other.is_valid()) { + return invalidate(); + } + grams = std::min(grams, other.grams); + vm::Dictionary dict1{extra, 32}, dict2(other.extra, 32); + bool ok = dict1.check_for_each([&](td::Ref cs1, td::ConstBitPtr key, int n) { + CHECK(n == 32); + td::Ref cs2 = dict2.lookup(key, 32); + td::RefInt256 val1 = tlb::t_VarUIntegerPos_32.as_integer(cs1); + if (val1.is_null()) { + return false; + } + td::RefInt256 val2 = cs2.is_null() ? td::zero_refint() : tlb::t_VarUIntegerPos_32.as_integer(cs2); + if (val2.is_null()) { + return false; + } + if (val1 > val2) { + if (val2->sgn() == 0) { + dict1.lookup_delete(key, 32); + } else { + dict1.set(key, 32, cs2); + } + } + return true; + }); + extra = dict1.get_root_cell(); + return ok || invalidate(); +} + 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()) && diff --git a/crypto/block/block.h b/crypto/block/block.h index 56e6dd38a..f64f00a87 100644 --- a/crypto/block/block.h +++ b/crypto/block/block.h @@ -390,6 +390,7 @@ struct CurrencyCollection { CurrencyCollection operator-(const CurrencyCollection& other) const; CurrencyCollection operator-(CurrencyCollection&& other) const; CurrencyCollection operator-(td::RefInt256 other_grams) const; + bool clamp(const CurrencyCollection& other); bool store(vm::CellBuilder& cb) const; bool store_or_zero(vm::CellBuilder& cb) const; bool fetch(vm::CellSlice& cs); diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index a32bad52f..49325957d 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -2760,22 +2760,25 @@ int Transaction::try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap, LOG(DEBUG) << "cannot reserve a negative amount: " << reserve.to_str(); return -1; } - if (reserve.grams > ap.remaining_balance.grams) { - if (mode & 2) { - reserve.grams = ap.remaining_balance.grams; + if (mode & 2) { + if (cfg.reserve_extra_enabled) { + if (!reserve.clamp(ap.remaining_balance)) { + LOG(DEBUG) << "failed to clamp reserve amount" << mode; + return -1; + } } else { - LOG(DEBUG) << "cannot reserve " << reserve.grams << " nanograms : only " << ap.remaining_balance.grams - << " available"; - return 37; // not enough grams + reserve.grams = std::min(reserve.grams, ap.remaining_balance.grams); } } + if (reserve.grams > ap.remaining_balance.grams) { + LOG(DEBUG) << "cannot reserve " << reserve.grams << " nanograms : only " << ap.remaining_balance.grams + << " available"; + return 37; // not enough grams + } if (!block::sub_extra_currency(ap.remaining_balance.extra, reserve.extra, newc.extra)) { LOG(DEBUG) << "not enough extra currency to reserve: " << block::CurrencyCollection{0, reserve.extra}.to_str() << " required, only " << block::CurrencyCollection{0, ap.remaining_balance.extra}.to_str() << " available"; - if (mode & 2) { - // TODO: process (mode & 2) correctly by setting res_extra := inf (reserve.extra, ap.remaining_balance.extra) - } return 38; // not enough (extra) funds } newc.grams = ap.remaining_balance.grams - reserve.grams; @@ -3778,6 +3781,7 @@ td::Status FetchConfigParams::fetch_config_params( action_phase_cfg->bounce_on_fail_enabled = config.get_global_version() >= 4; action_phase_cfg->message_skip_enabled = config.get_global_version() >= 8; action_phase_cfg->disable_custom_fess = config.get_global_version() >= 8; + action_phase_cfg->reserve_extra_enabled = config.get_global_version() >= 9; action_phase_cfg->mc_blackhole_addr = config.get_burning_config().blackhole_addr; } { diff --git a/crypto/block/transaction.h b/crypto/block/transaction.h index 20d7cb291..0f6952dc7 100644 --- a/crypto/block/transaction.h +++ b/crypto/block/transaction.h @@ -169,6 +169,7 @@ struct ActionPhaseConfig { bool bounce_on_fail_enabled{false}; bool message_skip_enabled{false}; bool disable_custom_fess{false}; + bool reserve_extra_enabled{false}; td::optional mc_blackhole_addr; const MsgPrices& fetch_msg_prices(bool is_masterchain) const { return is_masterchain ? fwd_mc : fwd_std; diff --git a/doc/GlobalVersions.md b/doc/GlobalVersions.md index 5db1ab768..1739b73ad 100644 --- a/doc/GlobalVersions.md +++ b/doc/GlobalVersions.md @@ -122,4 +122,5 @@ Operations for working with Merkle proofs, where cells can have non-zero level a ### Other changes - Fix `RAWRESERVE` action with flag `4` (use original balance of the account) by explicitly setting `original_balance` to `balance - msg_balance_remaining`. - Previously it did not work if storage fee was greater than the original balance. -- Jumps to nested continuations of depth more than 8 consume 1 gas for eact subsequent continuation (this does not affect most of TVM code). \ No newline at end of file +- Jumps to nested continuations of depth more than 8 consume 1 gas for eact subsequent continuation (this does not affect most of TVM code). +- Support extra currencies in reserve action with `+2` mode. \ No newline at end of file diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index 9a66ea81f..9e4d406e6 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -1002,6 +1002,7 @@ bool ValidateQuery::fetch_config_params() { action_phase_cfg_.bounce_on_fail_enabled = config_->get_global_version() >= 4; action_phase_cfg_.message_skip_enabled = config_->get_global_version() >= 8; action_phase_cfg_.disable_custom_fess = config_->get_global_version() >= 8; + action_phase_cfg_.reserve_extra_enabled = config_->get_global_version() >= 9; action_phase_cfg_.mc_blackhole_addr = config_->get_burning_config().blackhole_addr; } { From 2ebc6d6a3c73f558630c8c87893830062e3a18ff Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Wed, 15 Jan 2025 07:45:04 +0000 Subject: [PATCH 13/38] Fix error processing in load_cell (#1467) --- crypto/vm/db/DynamicBagOfCellsDb.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/crypto/vm/db/DynamicBagOfCellsDb.cpp b/crypto/vm/db/DynamicBagOfCellsDb.cpp index d4deae4a8..093037583 100644 --- a/crypto/vm/db/DynamicBagOfCellsDb.cpp +++ b/crypto/vm/db/DynamicBagOfCellsDb.cpp @@ -100,8 +100,18 @@ class DynamicBagOfCellsDbImpl : public DynamicBagOfCellsDb, private ExtCellCreat return get_cell_info_lazy(level_mask, hash, depth).cell; } td::Result> load_cell(td::Slice hash) override { - TRY_RESULT(loaded_cell, get_cell_info_force(hash).cell->load_cell()); - return std::move(loaded_cell.data_cell); + auto info = hash_table_.get_if_exists(hash); + if (info && info->sync_with_db) { + TRY_RESULT(loaded_cell, info->cell->load_cell()); + return std::move(loaded_cell.data_cell); + } + TRY_RESULT(res, loader_->load(hash, true, *this)); + if (res.status != CellLoader::LoadResult::Ok) { + return td::Status::Error("cell not found"); + } + Ref cell = res.cell(); + hash_table_.apply(hash, [&](CellInfo &info) { update_cell_info_loaded(info, hash, std::move(res)); }); + return cell; } td::Result> load_root(td::Slice hash) override { return load_cell(hash); @@ -145,9 +155,6 @@ class DynamicBagOfCellsDbImpl : public DynamicBagOfCellsDb, private ExtCellCreat promise->set_result(std::move(cell)); }); } - CellInfo &get_cell_info_force(td::Slice hash) { - return hash_table_.apply(hash, [&](CellInfo &info) { update_cell_info_force(info, hash); }); - } CellInfo &get_cell_info_lazy(Cell::LevelMask level_mask, td::Slice hash, td::Slice depth) { return hash_table_.apply(hash.substr(hash.size() - Cell::hash_bytes), [&](CellInfo &info) { update_cell_info_lazy(info, level_mask, hash, depth); }); From 987c7ca04b204dcb6b962ec6bdb60c4579f07f19 Mon Sep 17 00:00:00 2001 From: dbaranovstonfi <136370214+dbaranovstonfi@users.noreply.github.com> Date: Wed, 15 Jan 2025 14:50:18 +0400 Subject: [PATCH 14/38] emulator: set libraries when libs is NOT empty (#1449) Co-authored-by: dbaranov34 --- emulator/emulator-extern.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emulator/emulator-extern.cpp b/emulator/emulator-extern.cpp index 52c374edb..4e5f17bf7 100644 --- a/emulator/emulator-extern.cpp +++ b/emulator/emulator-extern.cpp @@ -615,7 +615,7 @@ const char *tvm_emulator_emulate_run_method(uint32_t len, const char *params_boc emulator->set_vm_verbosity_level(0); emulator->set_gas_limit(gas_limit); emulator->set_c7_raw(c7->fetch(0).as_tuple()); - if (libs.is_empty()) { + if (!libs.is_empty()) { emulator->set_libraries(std::move(libs)); } auto result = emulator->run_get_method(int(method_id), stack); From 710514b8f1dbbace316b4677c08121e6d7f776fb Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Thu, 16 Jan 2025 06:42:05 +0000 Subject: [PATCH 15/38] Validate Merkle proofs and updates in TLB validate (#1479) * Validate Merkle proofs and updates in TLB validate * Fix out-of-bound access in tl_jni_object.cpp --- crypto/block/block.tlb | 2 +- crypto/tl/tlbc-gen-cpp.cpp | 2 +- crypto/tl/tlblib.cpp | 8 +++++++- tl/tl/tl_jni_object.cpp | 3 ++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index 560433bfe..b8b40827e 100644 --- a/crypto/block/block.tlb +++ b/crypto/block/block.tlb @@ -296,7 +296,7 @@ transaction$0111 account_addr:bits256 lt:uint64 total_fees:CurrencyCollection state_update:^(HASH_UPDATE Account) description:^TransactionDescr = Transaction; -!merkle_update#02 {X:Type} old_hash:bits256 new_hash:bits256 +!merkle_update#04 {X:Type} old_hash:bits256 new_hash:bits256 old_depth:uint16 new_depth:uint16 old:^X new:^X = MERKLE_UPDATE X; update_hashes#72 {X:Type} old_hash:bits256 new_hash:bits256 = HASH_UPDATE X; diff --git a/crypto/tl/tlbc-gen-cpp.cpp b/crypto/tl/tlbc-gen-cpp.cpp index 55b4a1c05..5730f1691 100644 --- a/crypto/tl/tlbc-gen-cpp.cpp +++ b/crypto/tl/tlbc-gen-cpp.cpp @@ -2074,7 +2074,7 @@ void CppTypeCode::generate_skip_field(const Constructor& constr, const Field& fi output_cpp_expr(ss, expr, 100); ss << '.'; } - ss << "validate_skip_ref(ops, cs, weak)" << tail; + ss << "validate_skip_ref(ops, cs, " << (constr.is_special ? "true" : "weak") << ")" << tail; actions += Action{ss.str()}; } diff --git a/crypto/tl/tlblib.cpp b/crypto/tl/tlblib.cpp index 0e0e56265..ee05d371a 100644 --- a/crypto/tl/tlblib.cpp +++ b/crypto/tl/tlblib.cpp @@ -133,7 +133,13 @@ bool TLB::validate_ref_internal(int* ops, Ref cell_ref, bool weak) con } bool is_special; auto cs = load_cell_slice_special(std::move(cell_ref), is_special); - return always_special() ? is_special : (is_special ? weak : (validate_skip(ops, cs) && cs.empty_ext())); + if (cs.special_type() == vm::Cell::SpecialType::PrunnedBranch && weak) { + return true; + } + if (always_special() != is_special) { + return false; + } + return validate_skip(ops, cs, weak) && cs.empty_ext(); } bool TLB::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const { diff --git a/tl/tl/tl_jni_object.cpp b/tl/tl/tl_jni_object.cpp index e7e69789c..26a94d627 100644 --- a/tl/tl/tl_jni_object.cpp +++ b/tl/tl/tl_jni_object.cpp @@ -115,8 +115,9 @@ static size_t get_utf8_from_utf16_length(const jchar *p, jsize len) { for (jsize i = 0; i < len; i++) { unsigned int cur = p[i]; if ((cur & 0xF800) == 0xD800) { + ++i; if (i < len) { - unsigned int next = p[++i]; + unsigned int next = p[i]; if ((next & 0xFC00) == 0xDC00 && (cur & 0x400) == 0) { result += 4; continue; From d3485e42b9e7b21a0fb432bbf3f3dc2e41b2b673 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Fri, 17 Jan 2025 12:26:53 +0300 Subject: [PATCH 16/38] Temporary increase gas limit for certain accounts --- crypto/block/transaction.cpp | 61 ++++++++++++++++++++++++++++-------- doc/GlobalVersions.md | 5 +-- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index 920433761..363524b0c 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -1145,31 +1145,64 @@ td::RefInt256 ComputePhaseConfig::compute_gas_price(td::uint64 gas_used) const { namespace transaction { /** - * Checks if it is required to increase gas_limit (from GasLimitsPrices config) to special_gas_limit * 2 - * from masterchain GasLimitsPrices config for the transaction. + * Checks if it is required to increase gas_limit (from GasLimitsPrices config) for the transaction * * In January 2024 a highload wallet of @wallet Telegram bot in mainnet was stuck because current gas limit (1M) is * not enough to clean up old queires, thus locking funds inside. * See comment in crypto/smartcont/highload-wallet-v2-code.fc for details on why this happened. * Account address: EQD_v9j1rlsuHHw2FIhcsCFFSD367ldfDdCKcsNmNpIRzUlu - * It was proposed to validators to increase gas limit for this account for a limited amount of time (until 2024-02-29). + * It was proposed to validators to increase gas limit for this account to 70M for a limited amount + * of time (until 2024-02-29). * It is activated by setting global version to 5 in ConfigParam 8. * This config change also activates new behavior for special accounts in masterchain. * + * In Augost 2024 it was decided to unlock other old highload wallets that got into the same situation. + * See https://t.me/tondev_news/129 + * It is activated by setting global version to 9. + * * @param cfg The compute phase configuration. * @param now The Unix time of the transaction. * @param account The account of the transaction. * - * @returns True if gas_limit override is required, false otherwise + * @returns Overridden gas limit or empty td::optional */ -static bool override_gas_limit(const ComputePhaseConfig& cfg, ton::UnixTime now, const Account& account) { - if (!cfg.special_gas_full) { - return false; +static td::optional override_gas_limit(const ComputePhaseConfig& cfg, ton::UnixTime now, + const Account& account) { + struct OverridenGasLimit { + td::uint64 new_limit; + int from_version; + ton::UnixTime until; + }; + static std::map, OverridenGasLimit> accounts = []() { + auto parse_addr = [](const char* s) -> std::pair { + auto r_addr = StdAddress::parse(td::Slice(s)); + r_addr.ensure(); + return {r_addr.ok().workchain, r_addr.ok().addr}; + }; + std::map, OverridenGasLimit> accounts; + + // Increase limit for EQD_v9j1rlsuHHw2FIhcsCFFSD367ldfDdCKcsNmNpIRzUlu until 2024-02-29 00:00:00 UTC + accounts[parse_addr("0:FFBFD8F5AE5B2E1C7C3614885CB02145483DFAEE575F0DD08A72C366369211CD")] = { + .new_limit = 70'000'000, .from_version = 5, .until = 1709164800}; + + // Increase limit for multiple accounts (https://t.me/tondev_news/129) until 2025-03-01 00:00:00 UTC + accounts[parse_addr("UQBeSl-dumOHieZ3DJkNKVkjeso7wZ0VpzR4LCbLGTQ8xr57")] = { + .new_limit = 70'000'000, .from_version = 9, .until = 1740787200}; + accounts[parse_addr("EQC3VcQ-43klww9UfimR58TBjBzk7GPupXQ3CNuthoNp-uTR")] = { + .new_limit = 70'000'000, .from_version = 9, .until = 1740787200}; + accounts[parse_addr("EQBhwBb8jvokGvfreHRRoeVxI237PrOJgyrsAhLA-4rBC_H5")] = { + .new_limit = 70'000'000, .from_version = 9, .until = 1740787200}; + accounts[parse_addr("EQCkoRp4OE-SFUoMEnYfL3vF43T3AzNfW8jyTC4yzk8cJqMS")] = { + .new_limit = 70'000'000, .from_version = 9, .until = 1740787200}; + accounts[parse_addr("EQBDanbCeUqI4_v-xrnAN0_I2wRvEIaLg1Qg2ZN5c6Zl1KOh")] = { + .new_limit = 70'000'000, .from_version = 9, .until = 1740787200}; + return accounts; + }(); + auto it = accounts.find({account.workchain, account.addr}); + if (it == accounts.end() || cfg.global_version < it->second.from_version || now >= it->second.until) { + return {}; } - ton::UnixTime until = 1709164800; // 2024-02-29 00:00:00 UTC - ton::WorkchainId wc = 0; - const char* addr_hex = "FFBFD8F5AE5B2E1C7C3614885CB02145483DFAEE575F0DD08A72C366369211CD"; - return now < until && account.workchain == wc && account.addr.to_hex() == addr_hex; + return it->second.new_limit; } /** @@ -1183,10 +1216,12 @@ static bool override_gas_limit(const ComputePhaseConfig& cfg, ton::UnixTime now, * @returns The amount of gas. */ td::uint64 Transaction::gas_bought_for(const ComputePhaseConfig& cfg, td::RefInt256 nanograms) { - if (override_gas_limit(cfg, now, account)) { + if (auto new_limit = override_gas_limit(cfg, now, account)) { gas_limit_overridden = true; // Same as ComputePhaseConfig::gas_bought for, but with other gas_limit and max_gas_threshold - auto gas_limit = cfg.mc_gas_prices.special_gas_limit * 2; + auto gas_limit = new_limit.value(); + LOG(INFO) << "overridding gas limit for account " << account.workchain << ":" << account.addr.to_hex() << " to " + << gas_limit; auto max_gas_threshold = compute_max_gas_threshold(cfg.gas_price256, gas_limit, cfg.flat_gas_limit, cfg.flat_gas_price); if (nanograms.is_null() || sgn(nanograms) < 0) { diff --git a/doc/GlobalVersions.md b/doc/GlobalVersions.md index 64b2342a4..2048eee23 100644 --- a/doc/GlobalVersions.md +++ b/doc/GlobalVersions.md @@ -48,7 +48,7 @@ Version 5 enables higher gas limits for special contracts. Previously only ticktock transactions had this limit, while ordinary transactions had a default limit of `gas_limit` gas (1M). * Gas usage of special contracts is not taken into account when checking block limits. This allows keeping masterchain block limits low while having high gas limits for elector. -* Gas limit on `EQD_v9j1rlsuHHw2FIhcsCFFSD367ldfDdCKcsNmNpIRzUlu` is increased to `special_gas_limit * 2` until 2024-02-29. +* Gas limit on `EQD_v9j1rlsuHHw2FIhcsCFFSD367ldfDdCKcsNmNpIRzUlu` is increased to 70M (`special_gas_limit * 2`) until 2024-02-29. See [this post](https://t.me/tonstatus/88) for details. ### Loading libraries @@ -131,4 +131,5 @@ Example: if the last masterchain block seqno is `19071` then the list contains b - Jumps to nested continuations of depth more than 8 consume 1 gas for eact subsequent continuation (this does not affect most of TVM code). - Fix exception code in some TVM instructions: now `stk_und` has priority over other error codes. - `PFXDICTADD`, `PFXDICTSET`, `PFXDICTREPLACE`, `PFXDICTDEL`, `GETGASFEE`, `GETSTORAGEFEE`, `GETFORWARDFEE`, `GETORIGINALFWDFEE`, `GETGASFEESIMPLE`, `GETFORWARDFEESIMPLE`, `HASHEXT` -- Now setting the contract code to a library cell does not consume additional gas on execution of the code. \ No newline at end of file +- Now setting the contract code to a library cell does not consume additional gas on execution of the code. +- Temporary increase gas limit for some accounts (see [this post](https://t.me/tondev_news/129) for details, `override_gas_limit` in `transaction.cpp` for the list of accounts). \ No newline at end of file From 2d603f1f479529ad611b268988f9150ee1f87e93 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Fri, 17 Jan 2025 12:46:58 +0300 Subject: [PATCH 17/38] Adjust overridden gas limit --- crypto/block/transaction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index 836657f1a..92e20fb0b 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -1195,7 +1195,7 @@ static td::optional override_gas_limit(const ComputePhaseConfig& cfg accounts[parse_addr("EQCkoRp4OE-SFUoMEnYfL3vF43T3AzNfW8jyTC4yzk8cJqMS")] = { .new_limit = 70'000'000, .from_version = 9, .until = 1740787200}; accounts[parse_addr("EQBDanbCeUqI4_v-xrnAN0_I2wRvEIaLg1Qg2ZN5c6Zl1KOh")] = { - .new_limit = 70'000'000, .from_version = 9, .until = 1740787200}; + .new_limit = 225'000'000, .from_version = 9, .until = 1740787200}; return accounts; }(); auto it = accounts.find({account.workchain, account.addr}); From a224491179d7a2613a9929e491fe7d84bf8ca7e6 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Fri, 17 Jan 2025 12:58:15 +0000 Subject: [PATCH 18/38] Fix error processing in StaticBagOfCellsDb (#1481) --- crypto/vm/db/StaticBagOfCellsDb.cpp | 46 +++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/crypto/vm/db/StaticBagOfCellsDb.cpp b/crypto/vm/db/StaticBagOfCellsDb.cpp index c667f334b..80dbfbf0b 100644 --- a/crypto/vm/db/StaticBagOfCellsDb.cpp +++ b/crypto/vm/db/StaticBagOfCellsDb.cpp @@ -309,7 +309,9 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb { return 0; } td::Slice offset_view; - CHECK(info_.offset_byte_size <= 8); + if (info_.offset_byte_size > 8) { + return td::Status::Error(PSTRING() << "bag-of-cell error: invalid offset_byte_size " << info_.offset_byte_size); + } char arr[8]; td::RwMutex::ReadLock guard; if (info_.has_index) { @@ -321,19 +323,25 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb { offset_view = td::Slice(index_data_).substr((td::int64)idx * info_.offset_byte_size, info_.offset_byte_size); } - CHECK(offset_view.size() == (size_t)info_.offset_byte_size); + if (offset_view.size() != (size_t)info_.offset_byte_size) { + return td::Status::Error(PSTRING() << "bag-of-cell error: invalid offset view size" << offset_view.size()); + } return td::narrow_cast(info_.read_offset(offset_view.ubegin())); } td::Result load_root_idx(int root_i) { - CHECK(root_i >= 0 && root_i < info_.root_count); + if (root_i < 0 || root_i >= info_.root_count) { + return td::Status::Error(PSTRING() << "bag-of-cell error: invalid root index " << root_i); + } if (!info_.has_roots) { return 0; } char arr[8]; TRY_RESULT(idx_view, data_.view(td::MutableSlice(arr, info_.ref_byte_size), info_.roots_offset + (td::int64)root_i * info_.ref_byte_size)); - CHECK(idx_view.size() == (size_t)info_.ref_byte_size); + if (idx_view.size() != (size_t)info_.ref_byte_size) { + return td::Status::Error(PSTRING() << "bag-of-cell error: invalid idx_view size" << idx_view.size()); + } return info_.read_ref(idx_view.ubegin()); } @@ -343,8 +351,9 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb { bool should_cache; }; td::Result get_cell_location(int idx) { - CHECK(idx >= 0); - CHECK(idx < info_.cell_count); + if (idx < 0 || idx >= info_.cell_count) { + return td::Status::Error(PSTRING() << "bag-of-cell error: invalid cell index " << idx); + } TRY_STATUS(preload_index(idx)); TRY_RESULT(from, load_idx_offset(idx - 1)); TRY_RESULT(till, load_idx_offset(idx)); @@ -357,10 +366,15 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb { res.should_cache = res.end % 2 == 1; res.end /= 2; } - CHECK(std::numeric_limits::max() - res.begin >= info_.data_offset); - CHECK(std::numeric_limits::max() - res.end >= info_.data_offset); + if (std::numeric_limits::max() - res.begin < info_.data_offset || + std::numeric_limits::max() - res.end < info_.data_offset) { + return td::Status::Error(PSTRING() << "bag-of-cell error: invalid cell location (1) " << res.begin << ":" << res.end); + } res.begin += static_cast(info_.data_offset); res.end += static_cast(info_.data_offset); + if (res.begin > res.end) { + return td::Status::Error(PSTRING() << "bag-of-cell error: invalid cell location (2) " << res.begin << ":" << res.end); + } return res; } @@ -396,8 +410,6 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb { if (info_.has_index) { return td::Status::OK(); } - - CHECK(idx < info_.cell_count); if (index_i_.load(std::memory_order_relaxed) > idx) { return td::Status::OK(); } @@ -407,12 +419,17 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb { auto buf_slice = td::MutableSlice(buf.data(), buf.size()); for (; index_i_ <= idx; index_i_++) { auto offset = td::narrow_cast(info_.data_offset + index_offset_); - CHECK(data_.size() >= offset); + if (data_.size() < offset) { + return td::Status::Error(PSLICE() << "bag-of-cells error: invalid offset " << offset + << " (size=" << data_.size() << ")"); + } TRY_RESULT(cell, data_.view(buf_slice.copy().truncate(data_.size() - offset), offset)); CellSerializationInfo cell_info; TRY_STATUS(cell_info.init(cell, info_.ref_byte_size)); index_offset_ += cell_info.end_offset; - LOG_CHECK((unsigned)info_.offset_byte_size <= 8) << info_.offset_byte_size; + if ((unsigned)info_.offset_byte_size > 8) { + return td::Status::Error(PSTRING() << "bag-of-cell error: invalid offset_byte_size " << info_.offset_byte_size); + } td::uint8 tmp[8]; info_.write_offset(tmp, index_offset_); auto guard = index_data_rw_mutex_.lock_write(); @@ -488,7 +505,10 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb { bool should_cache) { deserialize_cell_cnt_.add(1); Ref refs[4]; - CHECK(cell_info.refs_cnt <= 4); + if (cell_info.refs_cnt > 4) { + return td::Status::Error(PSLICE() << "invalid bag-of-cells cell #" << idx << " has " << cell_info.refs_cnt + << " refs"); + } auto* ref_ptr = cell_slice.ubegin() + cell_info.refs_offset; for (int k = 0; k < cell_info.refs_cnt; k++, ref_ptr += info_.ref_byte_size) { int ref_idx = td::narrow_cast(info_.read_ref(ref_ptr)); From e0605156defc570bebf41b8d0032e9cbb1ec46b1 Mon Sep 17 00:00:00 2001 From: neodix42 Date: Tue, 21 Jan 2025 12:27:25 +0400 Subject: [PATCH 19/38] Reworked TON portable artifacts (#1486) * improve windows builds * install nasm for openssl compilation on win * install nasm for openssl compilation on win for github * add create-state, proxy-liteserver, rldp-http-proxy, http-proxy, adnl-proxy, dht-server, libtonlibjson.so and libemulator.so to docker image * build new artifacts inside Docker * add files smartcont/auto/* to docker image * build arm64 in docker branch build * improve secp256k1 build * adding natively portable binaries (all statically linked with libc++, without nixpkgs help) for x86-64 linux * install missing headers on ubuntu 20.04 * use clang-16 on ubuntu 20.04 * remove gsl for portable artifacts; add -f key to generate-random-id in order to read addr_list from file; * typo * decode json * decode json * add github workflow for appimages creation * add missing dependencies * use libc++ for appimages artifacts * typo * appimages wihtout libc++ * appimages with libc++ and some checks * add appimages to release (for testing) * add appimages to release (for testing) * add appimages to release (for testing) * add appimages to release (for testing) 2 * add appimages to release (for testing) 3 * appimages only on ubuntu 22 with ssl-3 for now * appimages only on ubuntu 20 with ssl-3 for now * appimages only on ubuntu 20 with ssl-3 for now * add export LD_LIBRARY_PATH="${APPDIR}/usr/lib:${LD_LIBRARY_PATH}" to appimage AppRun * create release * appimages without jemalloc * bind specific libraries to appimages * add libreadline * add plain portable libs * add proper /lib/x86_64-linux-gnu/libreadline.so.8 * app images build with libc * try to ensure ABI compatibility with older glibc * try to ensure ABI compatibility with older glibc for shared libraries * shared lib without libc but with D_GLIBCXX_USE_CXX11_ABI and -static-libgcc -static-libstdc++ * add -fPIC -fcommon * add /lib/x86_64-linux-gnu/libstdc++.so.6 to static binaries * add -static-libgcc -static-libstdc++ to libtonlibjson and emulator when PORTABLE=1 * add -static-libgcc -static-libstdc++ to libtonlibjson and emulator when PORTABLE=1 * update emulator portable * Update CMakeLists.txt * test portable macos binaries * do not use -static-libgcc -static-libstdc++ on mac for shared libs * do not use -static-libgcc -static-libstdc++ on mac for shared libs * adjust create-release.yml * minor fixes, typos * minor fixes * linux apps double check * avoid infinite loop when place in system bin dir * avoid infinite loop when place in system bin dir 2 * test compilation on linux arm64 * test appimages on arm64 linux * test appimages on arm64 linux 2 * add portable linux arm64 to release * clean up * update README.md --- .github/script/amd64-20.04.Dockerfile | 20 -- .github/script/amd64-22.04.Dockerfile | 20 -- .github/script/arm64-20.04.Dockerfile | 20 -- .github/script/arm64-22.04.Dockerfile | 20 -- .../build-ton-linux-arm64-appimage.yml | 57 ++++ .../build-ton-linux-arm64-shared.yml | 43 +++ .../build-ton-linux-x86-64-appimage.yml | 63 +++++ .../build-ton-macos-13-x86-64-portable.yml | 27 ++ .../build-ton-macos-14-arm64-portable.yml | 27 ++ .../workflows/build-ton-wasm-emscripten.yml | 2 +- .github/workflows/create-release.yml | 244 +++++++++++++----- .github/workflows/ton-arm64-macos.yml | 40 --- .github/workflows/ton-x86-64-linux.yml | 44 ---- .github/workflows/ton-x86-64-macos.yml | 40 --- .github/workflows/ton-x86-64-windows.yml | 2 +- README.md | 18 +- assembly/appimage/AppRun | 3 + assembly/appimage/create-appimages.sh | 50 ++++ assembly/appimage/ton.png | Bin 0 -> 5571 bytes assembly/cicd/jenkins/test-builds.groovy | 237 ----------------- assembly/native/build-ubuntu-appimages.sh | 109 ++++++++ assembly/native/build-ubuntu-portable-libs.sh | 132 ++++++++++ assembly/native/build-ubuntu-portable.sh | 8 +- emulator/CMakeLists.txt | 7 +- tonlib/CMakeLists.txt | 7 +- 25 files changed, 718 insertions(+), 522 deletions(-) delete mode 100644 .github/script/amd64-20.04.Dockerfile delete mode 100644 .github/script/amd64-22.04.Dockerfile delete mode 100644 .github/script/arm64-20.04.Dockerfile delete mode 100644 .github/script/arm64-22.04.Dockerfile create mode 100644 .github/workflows/build-ton-linux-arm64-appimage.yml create mode 100644 .github/workflows/build-ton-linux-arm64-shared.yml create mode 100644 .github/workflows/build-ton-linux-x86-64-appimage.yml create mode 100644 .github/workflows/build-ton-macos-13-x86-64-portable.yml create mode 100644 .github/workflows/build-ton-macos-14-arm64-portable.yml delete mode 100644 .github/workflows/ton-arm64-macos.yml delete mode 100644 .github/workflows/ton-x86-64-linux.yml delete mode 100644 .github/workflows/ton-x86-64-macos.yml create mode 100644 assembly/appimage/AppRun create mode 100644 assembly/appimage/create-appimages.sh create mode 100644 assembly/appimage/ton.png delete mode 100644 assembly/cicd/jenkins/test-builds.groovy create mode 100644 assembly/native/build-ubuntu-appimages.sh create mode 100644 assembly/native/build-ubuntu-portable-libs.sh diff --git a/.github/script/amd64-20.04.Dockerfile b/.github/script/amd64-20.04.Dockerfile deleted file mode 100644 index 1ec89ebd7..000000000 --- a/.github/script/amd64-20.04.Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM ubuntu:20.04 - -RUN apt update -RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata -RUN apt install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git curl libreadline-dev ccache libmicrohttpd-dev ninja-build libsecp256k1-dev libsodium-dev pkg-config - -WORKDIR / - -ARG BRANCH -ARG REPO -RUN git clone --recurse-submodules https://github.com/$REPO ton && cd ton && git checkout $BRANCH && git submodule update - -WORKDIR /ton -RUN mkdir /ton/build -WORKDIR /ton/build -ENV CC clang -ENV CXX clang++ -ENV CCACHE_DISABLE 1 -RUN cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DPORTABLE=1 -DTON_ARCH= -DCMAKE_CXX_FLAGS="-mavx2" .. -RUN ninja storage-daemon storage-daemon-cli tonlibjson blockchain-explorer fift func tolk validator-engine validator-engine-console create-state generate-random-id create-hardfork dht-server lite-client diff --git a/.github/script/amd64-22.04.Dockerfile b/.github/script/amd64-22.04.Dockerfile deleted file mode 100644 index 6134d1673..000000000 --- a/.github/script/amd64-22.04.Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM ubuntu:22.04 - -RUN apt update -RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata -RUN apt install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git curl libreadline-dev ccache libmicrohttpd-dev ninja-build libsecp256k1-dev libsodium-dev pkg-config - -WORKDIR / - -ARG BRANCH -ARG REPO -RUN git clone --recurse-submodules https://github.com/$REPO ton && cd ton && git checkout $BRANCH && git submodule update - -WORKDIR /ton -RUN mkdir /ton/build -WORKDIR /ton/build -ENV CC clang -ENV CXX clang++ -ENV CCACHE_DISABLE 1 -RUN cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DPORTABLE=1 -DTON_ARCH= -DCMAKE_CXX_FLAGS="-mavx2" .. -RUN ninja storage-daemon storage-daemon-cli tonlibjson blockchain-explorer fift func tolk validator-engine validator-engine-console create-state generate-random-id create-hardfork dht-server lite-client diff --git a/.github/script/arm64-20.04.Dockerfile b/.github/script/arm64-20.04.Dockerfile deleted file mode 100644 index 5e3505345..000000000 --- a/.github/script/arm64-20.04.Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM ubuntu:20.04 - -RUN apt update -RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata -RUN apt install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git curl libreadline-dev ccache libmicrohttpd-dev ninja-build libsecp256k1-dev libsodium-dev pkg-config - -WORKDIR / - -ARG BRANCH -ARG REPO -RUN git clone --recurse-submodules https://github.com/$REPO ton && cd ton && git checkout $BRANCH && git submodule update - -WORKDIR /ton -RUN mkdir /ton/build -WORKDIR /ton/build -ENV CC clang -ENV CXX clang++ -ENV CCACHE_DISABLE 1 -RUN cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DPORTABLE=1 -DTON_ARCH= .. -RUN ninja storage-daemon storage-daemon-cli tonlibjson blockchain-explorer fift func tolk validator-engine validator-engine-console create-state generate-random-id dht-server lite-client diff --git a/.github/script/arm64-22.04.Dockerfile b/.github/script/arm64-22.04.Dockerfile deleted file mode 100644 index f9805849d..000000000 --- a/.github/script/arm64-22.04.Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM ubuntu:22.04 - -RUN apt update -RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata -RUN apt install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git curl libreadline-dev ccache libmicrohttpd-dev ninja-build libsecp256k1-dev libsodium-dev pkg-config - -WORKDIR / - -ARG BRANCH -ARG REPO -RUN git clone --recurse-submodules https://github.com/$REPO ton && cd ton && git checkout $BRANCH && git submodule update - -WORKDIR /ton -RUN mkdir /ton/build -WORKDIR /ton/build -ENV CC clang -ENV CXX clang++ -ENV CCACHE_DISABLE 1 -RUN cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DPORTABLE=1 -DTON_ARCH= .. -RUN ninja storage-daemon storage-daemon-cli tonlibjson blockchain-explorer fift func tolk validator-engine validator-engine-console create-state generate-random-id dht-server lite-client diff --git a/.github/workflows/build-ton-linux-arm64-appimage.yml b/.github/workflows/build-ton-linux-arm64-appimage.yml new file mode 100644 index 000000000..d464d8a2a --- /dev/null +++ b/.github/workflows/build-ton-linux-arm64-appimage.yml @@ -0,0 +1,57 @@ +name: Ubuntu TON build (AppImages, arm64) + +on: [push,workflow_dispatch,workflow_call] + +jobs: + build: + runs-on: ubuntu-22.04-arm + + steps: + - name: Check out repository + uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - name: Install system libraries + run: | + sudo apt update + sudo apt install -y build-essential git cmake ninja-build zlib1g-dev libsecp256k1-dev libmicrohttpd-dev libsodium-dev liblz4-dev + sudo apt remove libgsl-dev + + - name: Install clang-16 + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 16 all + + - name: Build TON + run: | + git submodule sync --recursive + git submodule update + cp assembly/native/build-ubuntu-appimages.sh . + chmod +x build-ubuntu-appimages.sh + ./build-ubuntu-appimages.sh -a + + - name: Make AppImages + run: | + cp assembly/appimage/create-appimages.sh . + cp assembly/appimage/AppRun . + cp assembly/appimage/ton.png . + chmod +x create-appimages.sh + ./create-appimages.sh aarch64 + rm -rf artifacts + + + - name: Build TON libs + run: | + cp assembly/native/build-ubuntu-portable-libs.sh . + chmod +x build-ubuntu-portable-libs.sh + ./build-ubuntu-portable-libs.sh -a + cp ./artifacts/libtonlibjson.so appimages/artifacts/ + cp ./artifacts/libemulator.so appimages/artifacts/ + + - name: Upload artifacts + uses: actions/upload-artifact@master + with: + name: ton-arm64-linux + path: appimages/artifacts diff --git a/.github/workflows/build-ton-linux-arm64-shared.yml b/.github/workflows/build-ton-linux-arm64-shared.yml new file mode 100644 index 000000000..6433df0b5 --- /dev/null +++ b/.github/workflows/build-ton-linux-arm64-shared.yml @@ -0,0 +1,43 @@ +name: Ubuntu TON build (shared, arm64) + +on: [push,workflow_dispatch,workflow_call] + +jobs: + build: + strategy: + fail-fast: false + matrix: + os: [ubuntu-22.04-arm, ubuntu-24.04-arm] + runs-on: ${{ matrix.os }} + + steps: + - name: Check out repository + uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - name: Install system libraries + run: | + sudo apt-get update + sudo apt-get install -y build-essential git cmake ninja-build zlib1g-dev libsecp256k1-dev libmicrohttpd-dev libsodium-dev liblz4-dev libjemalloc-dev + + - if: matrix.os != 'ubuntu-24.04-arm' + name: Install llvm-16 + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 16 all + + - name: Build TON + run: | + git submodule sync --recursive + git submodule update + cp assembly/native/build-ubuntu-shared.sh . + chmod +x build-ubuntu-shared.sh + ./build-ubuntu-shared.sh -t -a + + - name: Upload artifacts + uses: actions/upload-artifact@master + with: + name: ton-binaries-${{ matrix.os }} + path: artifacts diff --git a/.github/workflows/build-ton-linux-x86-64-appimage.yml b/.github/workflows/build-ton-linux-x86-64-appimage.yml new file mode 100644 index 000000000..4f78ece9d --- /dev/null +++ b/.github/workflows/build-ton-linux-x86-64-appimage.yml @@ -0,0 +1,63 @@ +name: Ubuntu TON build (AppImages, x86-64) + +on: [push,workflow_dispatch,workflow_call] + +jobs: + build: + runs-on: ubuntu-20.04 + + steps: + - name: Check out repository + uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - name: Install system libraries + run: | + sudo apt update + sudo apt install -y build-essential git cmake ninja-build zlib1g-dev libsecp256k1-dev libmicrohttpd-dev libsodium-dev liblz4-dev + sudo apt remove libgsl-dev + + - name: Install gcc-11 g++-11 + run: | + sudo apt install -y manpages-dev software-properties-common + sudo add-apt-repository ppa:ubuntu-toolchain-r/test + sudo apt update && sudo apt install gcc-11 g++-11 + + - name: Install clang-16 + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 16 all + + - name: Build TON + run: | + git submodule sync --recursive + git submodule update + cp assembly/native/build-ubuntu-appimages.sh . + chmod +x build-ubuntu-appimages.sh + ./build-ubuntu-appimages.sh -a + + - name: Make AppImages + run: | + cp assembly/appimage/create-appimages.sh . + cp assembly/appimage/AppRun . + cp assembly/appimage/ton.png . + chmod +x create-appimages.sh + ./create-appimages.sh x86_64 + rm -rf artifacts + + + - name: Build TON libs + run: | + cp assembly/native/build-ubuntu-portable-libs.sh . + chmod +x build-ubuntu-portable-libs.sh + ./build-ubuntu-portable-libs.sh -a + cp ./artifacts/libtonlibjson.so appimages/artifacts/ + cp ./artifacts/libemulator.so appimages/artifacts/ + + - name: Upload artifacts + uses: actions/upload-artifact@master + with: + name: ton-x86_64-linux + path: appimages/artifacts diff --git a/.github/workflows/build-ton-macos-13-x86-64-portable.yml b/.github/workflows/build-ton-macos-13-x86-64-portable.yml new file mode 100644 index 000000000..5e50a4680 --- /dev/null +++ b/.github/workflows/build-ton-macos-13-x86-64-portable.yml @@ -0,0 +1,27 @@ +name: MacOS-13 TON build (portable, x86-64) + +on: [push,workflow_dispatch,workflow_call] + +jobs: + build: + runs-on: macos-13 + + steps: + - name: Check out repository + uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - name: Build TON + run: | + git submodule sync --recursive + git submodule update + cp assembly/native/build-macos-portable.sh . + chmod +x build-macos-portable.sh + ./build-macos-portable.sh -t -a + + - name: Upload artifacts + uses: actions/upload-artifact@master + with: + name: ton-x86_64-macos + path: artifacts diff --git a/.github/workflows/build-ton-macos-14-arm64-portable.yml b/.github/workflows/build-ton-macos-14-arm64-portable.yml new file mode 100644 index 000000000..8eb3af70a --- /dev/null +++ b/.github/workflows/build-ton-macos-14-arm64-portable.yml @@ -0,0 +1,27 @@ +name: MacOS-14 TON build (portable, arm64) + +on: [push,workflow_dispatch,workflow_call] + +jobs: + build: + runs-on: macos-14 + + steps: + - name: Check out repository + uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - name: Build TON + run: | + git submodule sync --recursive + git submodule update + cp assembly/native/build-macos-portable.sh . + chmod +x build-macos-portable.sh + ./build-macos-portable.sh -t -a + + - name: Upload artifacts + uses: actions/upload-artifact@master + with: + name: ton-arm64-macos + path: artifacts diff --git a/.github/workflows/build-ton-wasm-emscripten.yml b/.github/workflows/build-ton-wasm-emscripten.yml index 66fe5e93f..2ac1a2245 100644 --- a/.github/workflows/build-ton-wasm-emscripten.yml +++ b/.github/workflows/build-ton-wasm-emscripten.yml @@ -28,5 +28,5 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@master with: - name: ton-wasm-binaries + name: ton-wasm path: artifacts diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 04f81a248..3063ce06e 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -11,10 +11,26 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Download Linux arm64 artifacts + uses: dawidd6/action-download-artifact@v6 + with: + workflow: build-ton-linux-arm64-appimage.yml + path: artifacts + workflow_conclusion: success + skip_unpack: true + + - name: Download and unzip Linux arm64 artifacts + uses: dawidd6/action-download-artifact@v6 + with: + workflow: build-ton-linux-arm64-appimage.yml + path: artifacts + workflow_conclusion: success + skip_unpack: false + - name: Download Linux x86-64 artifacts uses: dawidd6/action-download-artifact@v6 with: - workflow: ton-x86-64-linux.yml + workflow: build-ton-linux-x86-64-appimage.yml path: artifacts workflow_conclusion: success skip_unpack: true @@ -22,7 +38,7 @@ jobs: - name: Download and unzip Linux x86-64 artifacts uses: dawidd6/action-download-artifact@v6 with: - workflow: ton-x86-64-linux.yml + workflow: build-ton-linux-x86-64-appimage.yml path: artifacts workflow_conclusion: success skip_unpack: false @@ -30,7 +46,7 @@ jobs: - name: Download Mac x86-64 artifacts uses: dawidd6/action-download-artifact@v6 with: - workflow: ton-x86-64-macos.yml + workflow: build-ton-macos-13-x86-64-portable.yml path: artifacts workflow_conclusion: success skip_unpack: true @@ -38,7 +54,7 @@ jobs: - name: Download Mac arm64 artifacts uses: dawidd6/action-download-artifact@v6 with: - workflow: ton-arm64-macos.yml + workflow: build-ton-macos-14-arm64-portable.yml path: artifacts workflow_conclusion: success skip_unpack: true @@ -46,7 +62,7 @@ jobs: - name: Download and unzip Mac x86-64 artifacts uses: dawidd6/action-download-artifact@v6 with: - workflow: ton-x86-64-macos.yml + workflow: build-ton-macos-13-x86-64-portable.yml path: artifacts workflow_conclusion: success skip_unpack: false @@ -54,7 +70,7 @@ jobs: - name: Download and unzip arm64 artifacts uses: dawidd6/action-download-artifact@v6 with: - workflow: ton-arm64-macos.yml + workflow: build-ton-macos-14-arm64-portable.yml path: artifacts workflow_conclusion: success skip_unpack: false @@ -147,7 +163,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-win-binaries.zip + file: artifacts/ton-x86-64-windows.zip asset_name: ton-win-x86-64.zip tag: ${{ steps.tag.outputs.TAG }} @@ -155,7 +171,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-win-binaries/fift.exe + file: artifacts/ton-x86-64-windows/fift.exe asset_name: fift.exe tag: ${{ steps.tag.outputs.TAG }} @@ -163,7 +179,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-win-binaries/func.exe + file: artifacts/ton-x86-64-windows/func.exe asset_name: func.exe tag: ${{ steps.tag.outputs.TAG }} @@ -171,7 +187,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-win-binaries/tolk.exe + file: artifacts/ton-x86-64-windows/tolk.exe asset_name: tolk.exe tag: ${{ steps.tag.outputs.TAG }} @@ -179,7 +195,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-win-binaries/lite-client.exe + file: artifacts/ton-x86-64-windows/lite-client.exe asset_name: lite-client.exe tag: ${{ steps.tag.outputs.TAG }} @@ -187,7 +203,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-win-binaries/proxy-liteserver.exe + file: artifacts/ton-x86-64-windows/proxy-liteserver.exe asset_name: proxy-liteserver.exe tag: ${{ steps.tag.outputs.TAG }} @@ -195,7 +211,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-win-binaries/rldp-http-proxy.exe + file: artifacts/ton-x86-64-windows/rldp-http-proxy.exe asset_name: rldp-http-proxy.exe tag: ${{ steps.tag.outputs.TAG }} @@ -203,7 +219,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-win-binaries/http-proxy.exe + file: artifacts/ton-x86-64-windows/http-proxy.exe asset_name: http-proxy.exe tag: ${{ steps.tag.outputs.TAG }} @@ -211,7 +227,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-win-binaries/storage-daemon-cli.exe + file: artifacts/ton-x86-64-windows/storage-daemon-cli.exe asset_name: storage-daemon-cli.exe tag: ${{ steps.tag.outputs.TAG }} @@ -219,7 +235,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-win-binaries/storage-daemon.exe + file: artifacts/ton-x86-64-windows/storage-daemon.exe asset_name: storage-daemon.exe tag: ${{ steps.tag.outputs.TAG }} @@ -227,7 +243,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-win-binaries/tonlibjson.dll + file: artifacts/ton-x86-64-windows/tonlibjson.dll asset_name: tonlibjson.dll tag: ${{ steps.tag.outputs.TAG }} @@ -235,7 +251,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-win-binaries/emulator.dll + file: artifacts/ton-x86-64-windows/emulator.dll asset_name: libemulator.dll tag: ${{ steps.tag.outputs.TAG }} @@ -243,7 +259,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-win-binaries/tonlib-cli.exe + file: artifacts/ton-x86-64-windows/tonlib-cli.exe asset_name: tonlib-cli.exe tag: ${{ steps.tag.outputs.TAG }} @@ -253,7 +269,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-macos-binaries.zip + file: artifacts/ton-x86_64-macos.zip asset_name: ton-mac-x86-64.zip tag: ${{ steps.tag.outputs.TAG }} @@ -261,7 +277,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-macos-binaries/fift + file: artifacts/ton-x86_64-macos/fift asset_name: fift-mac-x86-64 tag: ${{ steps.tag.outputs.TAG }} @@ -269,7 +285,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-macos-binaries/func + file: artifacts/ton-x86_64-macos/func asset_name: func-mac-x86-64 tag: ${{ steps.tag.outputs.TAG }} @@ -277,7 +293,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-macos-binaries/tolk + file: artifacts/ton-x86_64-macos/tolk asset_name: tolk-mac-x86-64 tag: ${{ steps.tag.outputs.TAG }} @@ -285,7 +301,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-macos-binaries/lite-client + file: artifacts/ton-x86_64-macos/lite-client asset_name: lite-client-mac-x86-64 tag: ${{ steps.tag.outputs.TAG }} @@ -293,7 +309,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-macos-binaries/proxy-liteserver + file: artifacts/ton-x86_64-macos/proxy-liteserver asset_name: proxy-liteserver-mac-x86-64 tag: ${{ steps.tag.outputs.TAG }} @@ -301,7 +317,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-macos-binaries/rldp-http-proxy + file: artifacts/ton-x86_64-macos/rldp-http-proxy asset_name: rldp-http-proxy-mac-x86-64 tag: ${{ steps.tag.outputs.TAG }} @@ -309,7 +325,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-macos-binaries/http-proxy + file: artifacts/ton-x86_64-macos/http-proxy asset_name: http-proxy-mac-x86-64 tag: ${{ steps.tag.outputs.TAG }} @@ -317,7 +333,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-macos-binaries/storage-daemon-cli + file: artifacts/ton-x86_64-macos/storage-daemon-cli asset_name: storage-daemon-cli-mac-x86-64 tag: ${{ steps.tag.outputs.TAG }} @@ -325,7 +341,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-macos-binaries/storage-daemon + file: artifacts/ton-x86_64-macos/storage-daemon asset_name: storage-daemon-mac-x86-64 tag: ${{ steps.tag.outputs.TAG }} @@ -333,7 +349,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-macos-binaries/libtonlibjson.dylib + file: artifacts/ton-x86_64-macos/libtonlibjson.dylib asset_name: tonlibjson-mac-x86-64.dylib tag: ${{ steps.tag.outputs.TAG }} @@ -341,7 +357,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-macos-binaries/libemulator.dylib + file: artifacts/ton-x86_64-macos/libemulator.dylib asset_name: libemulator-mac-x86-64.dylib tag: ${{ steps.tag.outputs.TAG }} @@ -349,7 +365,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-macos-binaries/tonlib-cli + file: artifacts/ton-x86_64-macos/tonlib-cli asset_name: tonlib-cli-mac-x86-64 tag: ${{ steps.tag.outputs.TAG }} @@ -360,7 +376,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-arm64-macos-binaries.zip + file: artifacts/ton-arm64-macos.zip asset_name: ton-mac-arm64.zip tag: ${{ steps.tag.outputs.TAG }} @@ -368,7 +384,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-arm64-macos-binaries/fift + file: artifacts/ton-arm64-macos/fift asset_name: fift-mac-arm64 tag: ${{ steps.tag.outputs.TAG }} @@ -376,7 +392,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-arm64-macos-binaries/func + file: artifacts/ton-arm64-macos/func asset_name: func-mac-arm64 tag: ${{ steps.tag.outputs.TAG }} @@ -384,7 +400,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-arm64-macos-binaries/tolk + file: artifacts/ton-arm64-macos/tolk asset_name: tolk-mac-arm64 tag: ${{ steps.tag.outputs.TAG }} @@ -392,7 +408,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-arm64-macos-binaries/lite-client + file: artifacts/ton-arm64-macos/lite-client asset_name: lite-client-mac-arm64 tag: ${{ steps.tag.outputs.TAG }} @@ -400,7 +416,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-arm64-macos-binaries/proxy-liteserver + file: artifacts/ton-arm64-macos/proxy-liteserver asset_name: proxy-liteserver-mac-arm64 tag: ${{ steps.tag.outputs.TAG }} @@ -408,7 +424,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-arm64-macos-binaries/rldp-http-proxy + file: artifacts/ton-arm64-macos/rldp-http-proxy asset_name: rldp-http-proxy-mac-arm64 tag: ${{ steps.tag.outputs.TAG }} @@ -416,7 +432,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-arm64-macos-binaries/http-proxy + file: artifacts/ton-arm64-macos/http-proxy asset_name: http-proxy-mac-arm64 tag: ${{ steps.tag.outputs.TAG }} @@ -424,7 +440,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-arm64-macos-binaries/storage-daemon-cli + file: artifacts/ton-arm64-macos/storage-daemon-cli asset_name: storage-daemon-cli-mac-arm64 tag: ${{ steps.tag.outputs.TAG }} @@ -432,7 +448,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-arm64-macos-binaries/storage-daemon + file: artifacts/ton-arm64-macos/storage-daemon asset_name: storage-daemon-mac-arm64 tag: ${{ steps.tag.outputs.TAG }} @@ -440,7 +456,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-arm64-macos-binaries/libtonlibjson.dylib + file: artifacts/ton-arm64-macos/libtonlibjson.dylib asset_name: tonlibjson-mac-arm64.dylib tag: ${{ steps.tag.outputs.TAG }} @@ -448,7 +464,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-arm64-macos-binaries/libemulator.dylib + file: artifacts/ton-arm64-macos/libemulator.dylib asset_name: libemulator-mac-arm64.dylib tag: ${{ steps.tag.outputs.TAG }} @@ -456,7 +472,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-arm64-macos-binaries/tonlib-cli + file: artifacts/ton-arm64-macos/tonlib-cli asset_name: tonlib-cli-mac-arm64 tag: ${{ steps.tag.outputs.TAG }} @@ -466,7 +482,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-linux-binaries.zip + file: artifacts/ton-x86_64-linux.zip asset_name: ton-linux-x86_64.zip tag: ${{ steps.tag.outputs.TAG }} @@ -474,7 +490,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-linux-binaries/fift + file: artifacts/ton-x86_64-linux/fift asset_name: fift-linux-x86_64 tag: ${{ steps.tag.outputs.TAG }} @@ -482,7 +498,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-linux-binaries/func + file: artifacts/ton-x86_64-linux/func asset_name: func-linux-x86_64 tag: ${{ steps.tag.outputs.TAG }} @@ -490,7 +506,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-linux-binaries/tolk + file: artifacts/ton-x86_64-linux/tolk asset_name: tolk-linux-x86_64 tag: ${{ steps.tag.outputs.TAG }} @@ -498,7 +514,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-linux-binaries/lite-client + file: artifacts/ton-x86_64-linux/lite-client asset_name: lite-client-linux-x86_64 tag: ${{ steps.tag.outputs.TAG }} @@ -506,7 +522,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-linux-binaries/proxy-liteserver + file: artifacts/ton-x86_64-linux/proxy-liteserver asset_name: proxy-liteserver-linux-x86_64 tag: ${{ steps.tag.outputs.TAG }} @@ -514,7 +530,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-linux-binaries/rldp-http-proxy + file: artifacts/ton-x86_64-linux/rldp-http-proxy asset_name: rldp-http-proxy-linux-x86_64 tag: ${{ steps.tag.outputs.TAG }} @@ -522,7 +538,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-linux-binaries/http-proxy + file: artifacts/ton-x86_64-linux/http-proxy asset_name: http-proxy-linux-x86_64 tag: ${{ steps.tag.outputs.TAG }} @@ -530,7 +546,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-linux-binaries/storage-daemon-cli + file: artifacts/ton-x86_64-linux/storage-daemon-cli asset_name: storage-daemon-cli-linux-x86_64 tag: ${{ steps.tag.outputs.TAG }} @@ -538,7 +554,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-linux-binaries/storage-daemon + file: artifacts/ton-x86_64-linux/storage-daemon asset_name: storage-daemon-linux-x86_64 tag: ${{ steps.tag.outputs.TAG }} @@ -546,7 +562,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-linux-binaries/libtonlibjson.so + file: artifacts/ton-x86_64-linux/libtonlibjson.so asset_name: tonlibjson-linux-x86_64.so tag: ${{ steps.tag.outputs.TAG }} @@ -554,7 +570,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-linux-binaries/libemulator.so + file: artifacts/ton-x86_64-linux/libemulator.so asset_name: libemulator-linux-x86_64.so tag: ${{ steps.tag.outputs.TAG }} @@ -562,16 +578,124 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-x86_64-linux-binaries/tonlib-cli + file: artifacts/ton-x86_64-linux/tonlib-cli asset_name: tonlib-cli-linux-x86_64 tag: ${{ steps.tag.outputs.TAG }} + + # linux arm64 + + - name: Upload Linux arm64 artifacts + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-arm64-linux.zip + asset_name: ton-linux-arm64.zip + tag: ${{ steps.tag.outputs.TAG }} + + - name: Upload Linux arm64 single artifact - fift + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-arm64-linux/fift + asset_name: fift-linux-arm64 + tag: ${{ steps.tag.outputs.TAG }} + + - name: Upload Linux arm64 single artifact - func + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-arm64-linux/func + asset_name: func-linux-arm64 + tag: ${{ steps.tag.outputs.TAG }} + + - name: Upload Linux arm64 single artifact - tolk + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-arm64-linux/tolk + asset_name: tolk-linux-arm64 + tag: ${{ steps.tag.outputs.TAG }} + + - name: Upload Linux arm64 single artifact - lite-client + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-arm64-linux/lite-client + asset_name: lite-client-linux-arm64 + tag: ${{ steps.tag.outputs.TAG }} + + - name: Upload Linux arm64 single artifact - proxy-liteserver + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-arm64-linux/proxy-liteserver + asset_name: proxy-liteserver-linux-arm64 + tag: ${{ steps.tag.outputs.TAG }} + + - name: Upload Linux arm64 single artifact - rldp-http-proxy + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-arm64-linux/rldp-http-proxy + asset_name: rldp-http-proxy-linux-arm64 + tag: ${{ steps.tag.outputs.TAG }} + + - name: Upload Linux arm64 single artifact - http-proxy + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-arm64-linux/http-proxy + asset_name: http-proxy-linux-arm64 + tag: ${{ steps.tag.outputs.TAG }} + + - name: Upload Linux arm64 single artifact - storage-daemon-cli + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-arm64-linux/storage-daemon-cli + asset_name: storage-daemon-cli-linux-arm64 + tag: ${{ steps.tag.outputs.TAG }} + + - name: Upload Linux arm64 single artifact - storage-daemon + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-arm64-linux/storage-daemon + asset_name: storage-daemon-linux-arm64 + tag: ${{ steps.tag.outputs.TAG }} + + - name: Upload Linux arm64 single artifact - tonlibjson + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-arm64-linux/libtonlibjson.so + asset_name: tonlibjson-linux-arm64.so + tag: ${{ steps.tag.outputs.TAG }} + + - name: Upload Linux arm64 single artifact - libemulator + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-arm64-linux/libemulator.so + asset_name: libemulator-linux-arm64.so + tag: ${{ steps.tag.outputs.TAG }} + + - name: Upload Linux arm64 single artifact - tonlib-cli + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-arm64-linux/tonlib-cli + asset_name: tonlib-cli-linux-arm64 + tag: ${{ steps.tag.outputs.TAG }} + + - name: Upload WASM artifacts uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: artifacts/ton-wasm-binaries.zip - asset_name: ton-wasm-binaries.zip + file: artifacts/ton-wasm.zip + asset_name: ton-wasm.zip tag: ${{ steps.tag.outputs.TAG }} - name: Upload Android Tonlib artifacts diff --git a/.github/workflows/ton-arm64-macos.yml b/.github/workflows/ton-arm64-macos.yml deleted file mode 100644 index f128680c2..000000000 --- a/.github/workflows/ton-arm64-macos.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: MacOS TON build (portable, arm64) - -on: [push,workflow_dispatch,workflow_call] - -jobs: - build: - runs-on: macos-14 - - steps: - - uses: actions/checkout@v3 - with: - submodules: 'recursive' - - - uses: cachix/install-nix-action@v23 - with: - extra_nix_config: | - access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} - - - name: Build TON - run: | - git submodule sync --recursive - git submodule update - cp assembly/nix/build-macos-nix.sh . - chmod +x build-macos-nix.sh - ./build-macos-nix.sh -t - - - name: Simple binaries test - run: | - sudo mv /nix/store /nix/store2 - artifacts/validator-engine -V - artifacts/lite-client -V - artifacts/fift -V - artifacts/func -V - artifacts/tolk -v - - - name: Upload artifacts - uses: actions/upload-artifact@master - with: - name: ton-arm64-macos-binaries - path: artifacts diff --git a/.github/workflows/ton-x86-64-linux.yml b/.github/workflows/ton-x86-64-linux.yml deleted file mode 100644 index 4cdafa114..000000000 --- a/.github/workflows/ton-x86-64-linux.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Ubuntu TON build (portable, x86-64) - -on: [push,workflow_dispatch,workflow_call] - -jobs: - build: - runs-on: ubuntu-22.04 - - steps: - - run: | - sudo apt update - sudo apt install -y apt-utils - - - uses: actions/checkout@v3 - with: - submodules: 'recursive' - - - uses: cachix/install-nix-action@v23 - with: - extra_nix_config: | - access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} - - - name: Build TON - run: | - git submodule sync --recursive - git submodule update - cp assembly/nix/build-linux-x86-64-nix.sh . - chmod +x build-linux-x86-64-nix.sh - ./build-linux-x86-64-nix.sh -t - - - name: Simple binaries test - run: | - sudo mv /nix/store /nix/store2 - artifacts/validator-engine -V - artifacts/lite-client -V - artifacts/fift -V - artifacts/func -V - artifacts/tolk -v - - - name: Upload artifacts - uses: actions/upload-artifact@master - with: - name: ton-x86_64-linux-binaries - path: artifacts diff --git a/.github/workflows/ton-x86-64-macos.yml b/.github/workflows/ton-x86-64-macos.yml deleted file mode 100644 index 41b8fa239..000000000 --- a/.github/workflows/ton-x86-64-macos.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: MacOS TON build (portable, x86-64) - -on: [push,workflow_dispatch,workflow_call] - -jobs: - build: - runs-on: macos-13 - - steps: - - uses: actions/checkout@v3 - with: - submodules: 'recursive' - - - uses: cachix/install-nix-action@v23 - with: - extra_nix_config: | - access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} - - - name: Build TON - run: | - git submodule sync --recursive - git submodule update - cp assembly/nix/build-macos-nix.sh . - chmod +x build-macos-nix.sh - ./build-macos-nix.sh -t - - - name: Simple binaries test - run: | - sudo mv /nix/store /nix/store2 - artifacts/validator-engine -V - artifacts/lite-client -V - artifacts/fift -V - artifacts/func -V - artifacts/tolk -v - - - name: Upload artifacts - uses: actions/upload-artifact@master - with: - name: ton-x86_64-macos-binaries - path: artifacts diff --git a/.github/workflows/ton-x86-64-windows.yml b/.github/workflows/ton-x86-64-windows.yml index c3c06f905..baaad778b 100644 --- a/.github/workflows/ton-x86-64-windows.yml +++ b/.github/workflows/ton-x86-64-windows.yml @@ -32,5 +32,5 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@master with: - name: ton-win-binaries + name: ton-x86-64-windows path: artifacts diff --git a/README.md b/README.md index 96516d447..d0aa8cb39 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ Usually, the response to your pull request will indicate which section it falls ## Build TON blockchain -### Ubuntu 20.4, 22.04 (x86-64, aarch64) +### Ubuntu 20.4, 22.04, 24.04 (x86-64, aarch64) Install additional system libraries ```bash sudo apt-get update @@ -141,18 +141,10 @@ Compile TON tonlib library ./build-android-tonlib.sh ``` -### Build TON portable binaries with Nix package manager -You need to install Nix first. -```bash - sh <(curl -L https://nixos.org/nix/install) --daemon -``` -Then compile TON with Nix by executing below command from the root folder: -```bash - cp -r assembly/nix/* . - export NIX_PATH=nixpkgs=https://github.com/nixOS/nixpkgs/archive/23.05.tar.gz - nix-build linux-x86-64-static.nix -``` -More examples for other platforms can be found under `assembly/nix`. +### TON portable binaries + +Linux portable binaries are wrapped into AppImages, at the same time MacOS portable binaries are statically linked executables. +Linux and MacOS binaries are available for both x86-64 and arm64 architectures. ## Running tests diff --git a/assembly/appimage/AppRun b/assembly/appimage/AppRun new file mode 100644 index 000000000..c7f147b3e --- /dev/null +++ b/assembly/appimage/AppRun @@ -0,0 +1,3 @@ +#!/bin/sh +export LD_LIBRARY_PATH="${APPDIR}/usr/lib:${LD_LIBRARY_PATH}" +exec "$(dirname $0)"/usr/bin/app "$@" diff --git a/assembly/appimage/create-appimages.sh b/assembly/appimage/create-appimages.sh new file mode 100644 index 000000000..2a8cd0ec6 --- /dev/null +++ b/assembly/appimage/create-appimages.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +if [ ! -d "artifacts" ]; then + echo "No artifacts found." + exit 2 +fi +# x86_64 or aarch64 +ARCH=$1 + +rm -rf appimages + +mkdir -p appimages/artifacts + +wget -nc https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$ARCH.AppImage +chmod +x ./appimagetool-$ARCH.AppImage + +cd appimages +for file in ../artifacts/*; do + if [[ -f "$file" && "$file" != *.so ]]; then + appName=$(basename "$file") + echo $appName + # prepare AppDir + mkdir -p $appName.AppDir/usr/{bin,lib} + cp ../AppRun $appName.AppDir/AppRun + sed -i "s/app/$appName/g" $appName.AppDir/AppRun + chmod +x ./$appName.AppDir/AppRun + printf '[Desktop Entry]\nName='$appName'\nExec='$appName'\nIcon='$appName'\nType=Application\nCategories=Utility;\n' > $appName.AppDir/$appName.desktop + cp ../ton.png $appName.AppDir/$appName.png + cp $file $appName.AppDir/usr/bin/ + cp ../build/openssl_3/libcrypto.so.3 \ + /lib/$ARCH-linux-gnu/libatomic.so.1 \ + /lib/$ARCH-linux-gnu/libsodium.so.23 \ + /lib/$ARCH-linux-gnu/libz.so.1 \ + /lib/$ARCH-linux-gnu/liblz4.so.1 \ + /lib/$ARCH-linux-gnu/libmicrohttpd.so.12 \ + /lib/$ARCH-linux-gnu/libreadline.so.8 \ + /lib/$ARCH-linux-gnu/libstdc++.so.6 \ + $appName.AppDir/usr/lib/ + + chmod +x ./$appName.AppDir/usr/bin/$appName + # create AppImage + ./../appimagetool-$ARCH.AppImage -l $appName.AppDir + mv $appName-$ARCH.AppImage artifacts/$appName + fi +done + +ls -larth artifacts +cp -r ../artifacts/{smartcont,lib} artifacts/ +pwd +ls -larth artifacts diff --git a/assembly/appimage/ton.png b/assembly/appimage/ton.png new file mode 100644 index 0000000000000000000000000000000000000000..2a25c863dbab04619e4ff51dd67612439d84f6f7 GIT binary patch literal 5571 zcmV;!6+G&RP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGf6951U69E94oEQKA6h5>%>9^eF z{EAB5>y)Iood3J`o_p>&ny%{}#0I+Jsh3a1{_~Ug>7lSE5%cIN@_$gGc|F1Kf>7*x zw};NB$kL6cj+A#D-Iwj}5$bKIKUclYepmum8chzMV@;N`hYUF9-+}05d;t zA6nAdM>;&Qe`V>~c}1jy(18M=qI}oYUI)MLg;VPH^fgW=9V9m>fSlC4*l{T?!^5cw zYweFzlMXTw6u<=DZ%f>LpqF%HNz+~8V3cC=A{#{M~)4UjvDM(QnF=Hm~<3I zX#t$>8D98m59z4N@%t+lSNTatJrosyw*4w;A=l8iskmrXO8~3h?mc`lK{`k_meu%= zuB{*)1yDr*T63>w1WlhBUqIhdPTEotf8!Nw5EN3v1gaT;mPtMwlrf)r`AFQ~kTsD7^R08N3N>;pprf)B88!Bf?6q+iJ z0MN7PB+~dyWe|Y&{1uO;kq+V*hUq+Z18ED3*aFa6qMpPc=^(BU4D0nJq%9z#dbnCY zkEiE6X#K8}h#4>9;JG;IiiVIQ0M2=MM9dkHU;>!4xBEi>2&8u&%B5ls#Q~>zlmk&OtL)=Y)PIZr<^-GEIT0iUJ&XvK}2oP-Q z7#6`Rg@6fYdSWX_`(+#n06+Zdz|VAumnjfhzf$n?y15VlTEBvD^3q)vKs1p;>sJ=z z)%PY`o`Jh5fcGyBm$Y<}4x%LGEggq043Ul-xV8vh^>)wUix}U7q^YY7ytBrgG(_$Q zpsJ;_D}lSb$T$^2Z&$qwyTvX{fo|N}ht`iwqC1hAvB!nP^iGceCA+$!!%5OXkckur zd;<-Yq~i#VjsTS{ooM|?9t`uSdcje@h;t)AMN3DI+deNu2+I6=Z=*xu5)O?3bq9LT z`VmU{MzrOv4izqOLI6j;h#zW0k0-*w(e~kY&pWX{VEYtkAdxD?-7W;gKz&uQ&6U7y z7XU^&AcbIe+^Eg+LhDBg!)_C@Q2^TZE2M=W1z@Wo*(HEc;bQhe#1b_7#sOOdP}Gc7 zDiDhaH{0S$v0eZtdq##vaJv_=nD|KQM7Q>> z(3(0(3qfqdS~Igu0B@a(dz_7|MifQY51g|sO2V==08V}oC$VZVV9f}C){i)hRVHVF z04iE=>W{cUWs3zGus{G9=#RKUcY-tup-=(fkS{fgbl0)Z#1_|5}Q=mkQG!TRz5`(0{0{o=2{?o~ z;pQ;uBiH!p$ymdO1JV?)q8tRM1aK*utl1-WuB-T#>W~lpSF|LOdT2+7(5d|{ZLFG6 zY(BxE5`e&;|3pfEY6fW|>}bzi_Jr7d<$ORh2$%~XKI{qa5>fzGots7k{Q|}tt3!U1 zQOsQ2B3l2~s(onv7GgZi#g#tN5r?8?^RqJMBY<$~*SA$0S4}#Ib)-_d&sZ1;h3R+D zWCYN5I#jF7DpFg~`t8E&)spGQD-xy#Y&Hc-id5XLdvleBp-Oh*)wEL&RgjJlm>FBCi|jo3`;F^8&H zTf#4{@B@|-E zr?IUc36lB8xO%AnNBhYYkAvYYtk>yi_Kl=Re-io_#c`H2FD-4jwTN`Ck!1uB#`-;; z4GSmV4-JNGrC*z0OD7R<>_1i}{kX0F`O~G?fsz{*#uwE1VLxeIhuBk~-OEZzi<?)U=Kw5$z{?Kir(uE$_p=g z?Y|-KAe}rSg$D_)kxBuxY50Y=AMr;$AMh6R$MGNm0rn^s?(HNkks;P|5L#!L2SQE2 z$l;0*X{by&G^GD@zraK2;V;Pc1rflScLfdsO6b7&wyw>1nAp=@4|xcV9#^n|RR?=B zjj{QI*fEmYbHLTuv)?QxEhTy8FQB(Cd{8a9e(~1;8NA+MP?nA-Z&x}v;;B0zw=Yx5 z99ab5@dS4Q1DT?YitdXIf^NKLNGk7HS^$Hx)P?%@L;aYmbUq z#n9XIO89&v6u_v98sZW?w?T;rxeFUXac_d}eC0w$0T^#s-GV1SxA78Ptvj80CsDi^-Aj?-gjom`yaoHk)C`6fD=3^_r+j0 zCFq$iN}m4hp!dv>%(Gs2*p^V0K+OT)j?{4?Cx;?lPeR@iV3Yw+q?2T2CtV?0;QRq}Be%UO zOr5wwP(lD_pr==Vdw)4;3Cr>CKyIOEhn)MqQ&Bj+7a3%3Uqx+8upVFWdvZ{Dl|YyIpp06oQgsMF!=b&bxHB45p)m$e*UF* zeNIFz0es`l9@1iEM6_@K&;>VtCi$k&RMZlH!R$SdiID!JVCThP7rf;|Nlu5;qnrS~ zhypkM>S~4Y2(1KNYROKuCO;XJ6MzBiZ0kE~gxM1~1iI9yoN@6cs3-vN+16;I07nI2 zdA`88B}GR`0YH5n5x)TbK|r3bbXG~PqL!fOZ5w=W5praVbC)RW^1xXoB|u#PEPET8 ze7CP_&MhP?}ll-vED05tIHhhO99!|_jF z4U-lNN=1EU<(5#)`~5p4PX%QP%q9AxuX9ve>BoOoLRw6y6!n>v@7)>pJ|KH8G#Y;R zYsf;76PGAPeS*1vUdTH=BFWM3Lip$ZQ_B^2FDP%Md{jrERi@&6E`=NOw zmXq?F*Zm_QspHX^uM6=9fbL zT%=jrO#cAZ&rNTGq)or&TawoWV&Z>59+Hy}IhsfD%zEZOck|0={S#>Z)->aP(*lTq zW5Of_79+4}VTiPBX5#|LA3D{C^PdS6jZXa0^s}L9I(tI>-OI{I%Vu7>oB0o6*iSA{ z0NL?2K~}@tmw;k&SZMl7=0A}Zv+`j-xfl}wucw^c<7$j~&aib6Br21MM{i^P6EHE$ z4l#HlfP;0o4xY>nyCJJaSUPe_jY(NCCdXDX%E)n_WTg*iw zVCl#ySwfNOw!{PHrSOi*vVEiO^OubsOGU7seP089YX2`vlS zvMC1mwdbSg`ZQr&0A&~tkrhy#(2++W>DlrC_=UA&3~p@Y5- zslxm)>^}~0TPa1`rU^!lQy>Naeii-kf(}eVejMbsQWktzEWrFK;B3eX;Gs;!90~`o&Eplf0a)24X_lD( zECOg?KXRcOL!8B!q3e@|@7|gv4>V4JfN0P2Gzg&U6H4|6_sIhlq=n={^NjFOEf3ee zG@N=cyGdskz?UmAHrkxP4)@R6CP29y6=Xqqc9X`TvmpNMxXVk=kisQ?Eg`1>I4OU^ zIsfpno6g4vj$qIm6?u#qM}R^JZFepDVBc8`doGlG0=T;dR|4iFTq?ATVLz1IH6x!H z=M%ttYta`(h4Z$E$=>Ms=-zy0Y&HcN&7qE%plQhh#H?n@gTt^Xr1A$u!@Vbmu* zSLiZ4Pzhj0v7;5>gvMT%C`Nr!Go3ES5A6dz|1hou=+Ly3CHb(XfIDCGTv$)Lg+(iX z*=6|cp~Yu|lm1W96z+V{Gpm#~h=IYZFK@z?04=w^VzH<4M$Gh2O~)e)vlRvb+z`Q) z04*nat=y|B-ib$E7^n&}ST)?Nw?UY{ z?2A0JycTGX@C6pSMCs96{y-2(A-F$KC_%N9$d>&rV6exr7 zQb`Lj<7!+tOnkvqk_0 z`&VLlNyH%z)>&q3mI+|(ypTt;?6)CEPBd-x9Ls8%T4}QSRy;S1*p9xHD%}Gm7JsvG zF@V^L!;F?~&CeWoi&)D(}>lM(i(BYP$domd_5ZuEk<|2m@3_D0T{HeFSX<}Cn6JuNnG0C!kH=;1Ypov zU%hs=Nez2M9ah%{43i)oXK{KBa6KPf9A5S|mVH*8L#xYH&bI7?7b|WGz#y7Pm9${> zD|P9wuPULbXKfoU3m^l=POAwIp<-@|Zw!o?QxHyGIu`;kKn*AbKd+k?0T>t$CB)I? zAhr)^#U+pFxL@rLOE5Esdudqn&1dsuj#qz=DESFOO7-&tM9v#c1 zNP*_%Hfb8a0x)>5ZD_>-EEFd;Z>%W0Z@z2$T&8d>fDAYt5*PlRdjhy!jE7i>aYLdp zEu`^p{RRSz0At`#APPd5eg=^o@5W=DUxsIkxn^!zKp^Wk5NQM$1M4XAh+#>OiI)mZ zNC6leIv=llx1V$nw>h$=6kMy_T|^Z?2CXIPNeq$>;tGMV-cUl?0wT5mGT@BJ9mJgT zOhN_$WI!L0lZds_&4WAwFu?O=4$xCyJx!vV22u$iLtFpIti2ddYCWgdmEXi!EN9_J zD1Z!?qscGr=^`DZFfMPZx*;OxSq*6gkRg@w_;%ph08Zl3=>e%vDM>DXF|?h%o@9b_ z5L0{seM>oMON}Z5$gp&O_s=ekkPecKyJq-5y4U(@q}-yG0LDO{=~SfgY%-y!05Y8I zOWpLc>Tc6B4_C}Dv%eAxuP7~mF@Ab7zVQRKB8!@r6gMo2kdDHj048v(E%EIGy`&=} z2kS~#J5qW`6i@&;nYp{8Jto7$nHkaAHo22~nSW3KR8;J`+A~brLSW45d+o0=Dgh_} zW`5p2w4{~CTowCQmad(L(;GHW0EIF%lG^-1-z(=mo}`0ggxCMjT&?-rWf4DSlPm}n zfL(OPlKajkp8oydiFhP^YKJw+m*({ZBe#SGzkg@sujd4BEb)%EYSb} literal 0 HcmV?d00001 diff --git a/assembly/cicd/jenkins/test-builds.groovy b/assembly/cicd/jenkins/test-builds.groovy deleted file mode 100644 index 0b5ab7a38..000000000 --- a/assembly/cicd/jenkins/test-builds.groovy +++ /dev/null @@ -1,237 +0,0 @@ -pipeline { - - agent none - stages { - stage('Run Builds') { - parallel { - stage('Ubuntu 20.04 x86-64 (shared)') { - agent { - label 'Ubuntu_x86-64' - } - steps { - timeout(time: 180, unit: 'MINUTES') { - sh ''' - cp assembly/native/build-ubuntu-shared.sh . - chmod +x build-ubuntu-shared.sh - ./build-ubuntu-shared.sh -a - ''' - sh ''' - cd artifacts - zip -9r ton-x86_64-linux-shared ./* - ''' - archiveArtifacts artifacts: 'artifacts/ton-x86_64-linux-shared.zip' - } - } - } - stage('Ubuntu 20.04 x86-64 (portable)') { - agent { - label 'Ubuntu_x86-64' - } - steps { - timeout(time: 180, unit: 'MINUTES') { - sh ''' - cp assembly/nix/build-linux-x86-64-nix.sh . - chmod +x build-linux-x86-64-nix.sh - ./build-linux-x86-64-nix.sh - ''' - sh ''' - cd artifacts - zip -9r ton-x86-64-linux-portable ./* - ''' - archiveArtifacts artifacts: 'artifacts/ton-x86-64-linux-portable.zip' - } - } - } - stage('Ubuntu 20.04 aarch64 (shared)') { - agent { - label 'Ubuntu_arm64' - } - steps { - timeout(time: 180, unit: 'MINUTES') { - sh ''' - cp assembly/native/build-ubuntu-shared.sh . - chmod +x build-ubuntu-shared.sh - ./build-ubuntu-shared.sh -a - ''' - sh ''' - cd artifacts - zip -9r ton-arm64-linux-shared ./* - ''' - archiveArtifacts artifacts: 'artifacts/ton-arm64-linux-shared.zip' - } - } - } - stage('Ubuntu 20.04 aarch64 (portable)') { - agent { - label 'Ubuntu_arm64' - } - steps { - timeout(time: 180, unit: 'MINUTES') { - sh ''' - cp assembly/nix/build-linux-arm64-nix.sh . - chmod +x build-linux-arm64-nix.sh - ./build-linux-arm64-nix.sh - ''' - sh ''' - cd artifacts - zip -9r ton-arm64-linux-portable ./* - ''' - archiveArtifacts artifacts: 'artifacts/ton-arm64-linux-portable.zip' - } - } - } - stage('macOS 12.7 x86-64 (shared)') { - agent { - label 'macOS_12.7_x86-64' - } - steps { - timeout(time: 180, unit: 'MINUTES') { - sh ''' - cp assembly/native/build-macos-shared.sh . - chmod +x build-macos-shared.sh - ./build-macos-shared.sh -a - ''' - sh ''' - cd artifacts - zip -9r ton-x86-64-macos-shared ./* - ''' - archiveArtifacts artifacts: 'artifacts/ton-x86-64-macos-shared.zip' - } - } - } - stage('macOS 12.7 x86-64 (portable)') { - agent { - label 'macOS_12.7_x86-64' - } - steps { - timeout(time: 180, unit: 'MINUTES') { - sh ''' - cp assembly/nix/build-macos-nix.sh . - chmod +x build-macos-nix.sh - ./build-macos-nix.sh - ''' - sh ''' - cd artifacts - zip -9r ton-x86-64-macos-portable ./* - ''' - archiveArtifacts artifacts: 'artifacts/ton-x86-64-macos-portable.zip' - } - } - } - stage('macOS 12.6 aarch64 (shared)') { - agent { - label 'macOS_12.6-arm64-m1' - } - steps { - timeout(time: 180, unit: 'MINUTES') { - sh ''' - cp assembly/native/build-macos-shared.sh . - chmod +x build-macos-shared.sh - ./build-macos-shared.sh -a - ''' - sh ''' - cd artifacts - zip -9r ton-arm64-macos-m1-shared ./* - ''' - archiveArtifacts artifacts: 'artifacts/ton-arm64-macos-m1-shared.zip' - } - } - } - stage('macOS 12.6 aarch64 (portable)') { - agent { - label 'macOS_12.6-arm64-m1' - } - steps { - timeout(time: 180, unit: 'MINUTES') { - sh ''' - cp assembly/nix/build-macos-nix.sh . - chmod +x build-macos-nix.sh - ./build-macos-nix.sh - ''' - sh ''' - cd artifacts - zip -9r ton-arm64-macos-portable ./* - ''' - archiveArtifacts artifacts: 'artifacts/ton-arm64-macos-portable.zip' - } - } - } - stage('macOS 13.2 aarch64 (shared)') { - agent { - label 'macOS_13.2-arm64-m2' - } - steps { - timeout(time: 180, unit: 'MINUTES') { - sh ''' - cp assembly/native/build-macos-shared.sh . - chmod +x build-macos-shared.sh - ./build-macos-shared.sh -a - ''' - sh ''' - cd artifacts - zip -9r ton-arm64-macos-m2-shared ./* - ''' - archiveArtifacts artifacts: 'artifacts/ton-arm64-macos-m2-shared.zip' - } - } - } - stage('Windows Server 2022 x86-64') { - agent { - label 'Windows_x86-64' - } - steps { - timeout(time: 180, unit: 'MINUTES') { - bat ''' - copy assembly\\native\\build-windows.bat . - build-windows.bat - ''' - bat ''' - cd artifacts - zip -9r ton-x86-64-windows ./* - ''' - archiveArtifacts artifacts: 'artifacts/ton-x86-64-windows.zip' - } - } - } - stage('Android Tonlib') { - agent { - label 'Ubuntu_x86-64' - } - steps { - timeout(time: 180, unit: 'MINUTES') { - sh ''' - cp assembly/android/build-android-tonlib.sh . - chmod +x build-android-tonlib.sh - ./build-android-tonlib.sh -a - ''' - sh ''' - cd artifacts/tonlib-android-jni - zip -9r ton-android-tonlib ./* - ''' - archiveArtifacts artifacts: 'artifacts/tonlib-android-jni/ton-android-tonlib.zip' - } - } - } - stage('WASM fift func emulator') { - agent { - label 'Ubuntu_x86-64' - } - steps { - timeout(time: 180, unit: 'MINUTES') { - sh ''' - cp assembly/wasm/fift-func-wasm-build-ubuntu.sh . - chmod +x fift-func-wasm-build-ubuntu.sh - ./fift-func-wasm-build-ubuntu.sh -a - ''' - sh ''' - cd artifacts - zip -9r ton-wasm-binaries ./* - ''' - archiveArtifacts artifacts: 'artifacts/ton-wasm-binaries.zip' - } - } - } - } - } - } -} diff --git a/assembly/native/build-ubuntu-appimages.sh b/assembly/native/build-ubuntu-appimages.sh new file mode 100644 index 000000000..4e63234d9 --- /dev/null +++ b/assembly/native/build-ubuntu-appimages.sh @@ -0,0 +1,109 @@ +#/bin/bash + +with_tests=false +with_artifacts=false + + +while getopts 'ta' flag; do + case "${flag}" in + t) with_tests=true ;; + a) with_artifacts=true ;; + *) break + ;; + esac +done + +if [ ! -d "build" ]; then + mkdir build + cd build +else + cd build + rm -rf .ninja* CMakeCache.txt +fi + +export CC=$(which clang-16) +export CXX=$(which clang++-16) +export CCACHE_DISABLE=1 + +if [ ! -d "openssl_3" ]; then + git clone https://github.com/openssl/openssl openssl_3 + cd openssl_3 + opensslPath=`pwd` + git checkout openssl-3.1.4 + ./config + make build_libs -j12 + test $? -eq 0 || { echo "Can't compile openssl_3"; exit 1; } + cd .. +else + opensslPath=$(pwd)/openssl_3 + echo "Using compiled openssl_3" +fi + +cmake -GNinja .. \ +-DCMAKE_BUILD_TYPE=Release \ +-DPORTABLE=1 \ +-DOPENSSL_ROOT_DIR=$opensslPath \ +-DOPENSSL_INCLUDE_DIR=$opensslPath/include \ +-DOPENSSL_CRYPTO_LIBRARY=$opensslPath/libcrypto.so + + +test $? -eq 0 || { echo "Can't configure ton"; exit 1; } + +if [ "$with_tests" = true ]; then +ninja storage-daemon storage-daemon-cli fift func tolk tonlib tonlibjson tonlib-cli \ + validator-engine lite-client pow-miner validator-engine-console blockchain-explorer \ + generate-random-id json2tlo dht-server http-proxy rldp-http-proxy \ + adnl-proxy create-state emulator test-ed25519 test-ed25519-crypto test-bigint \ + test-vm test-fift test-cells test-smartcont test-net test-tdactor test-tdutils \ + test-tonlib-offline test-adnl test-dht test-rldp test-rldp2 test-catchain \ + test-fec test-tddb test-db test-validator-session-state test-emulator proxy-liteserver + test $? -eq 0 || { echo "Can't compile ton"; exit 1; } +else +ninja storage-daemon storage-daemon-cli fift func tolk tonlib tonlibjson tonlib-cli \ + validator-engine lite-client pow-miner validator-engine-console blockchain-explorer \ + generate-random-id json2tlo dht-server http-proxy rldp-http-proxy \ + adnl-proxy create-state emulator proxy-liteserver + test $? -eq 0 || { echo "Can't compile ton"; exit 1; } +fi + +# simple binaries' test +./storage/storage-daemon/storage-daemon -V || exit 1 +./validator-engine/validator-engine -V || exit 1 +./lite-client/lite-client -V || exit 1 +./crypto/fift -V || exit 1 + +echo validator-engine +ldd ./validator-engine/validator-engine || exit 1 +ldd ./validator-engine-console/validator-engine-console || exit 1 +ldd ./crypto/fift || exit 1 +echo blockchain-explorer +ldd ./blockchain-explorer/blockchain-explorer || exit 1 +echo libtonlibjson.so +ldd ./tonlib/libtonlibjson.so.0.5 || exit 1 +echo libemulator.so +ldd ./emulator/libemulator.so || exit 1 + +cd .. + +if [ "$with_artifacts" = true ]; then + rm -rf artifacts + mkdir artifacts + mv build/tonlib/libtonlibjson.so.0.5 build/tonlib/libtonlibjson.so + cp build/storage/storage-daemon/storage-daemon build/storage/storage-daemon/storage-daemon-cli \ + build/crypto/fift build/crypto/tlbc build/crypto/func build/tolk/tolk build/crypto/create-state build/blockchain-explorer/blockchain-explorer \ + build/validator-engine-console/validator-engine-console build/tonlib/tonlib-cli build/utils/proxy-liteserver \ + build/tonlib/libtonlibjson.so build/http/http-proxy build/rldp-http-proxy/rldp-http-proxy \ + build/dht-server/dht-server build/lite-client/lite-client build/validator-engine/validator-engine \ + build/utils/generate-random-id build/utils/json2tlo build/adnl/adnl-proxy build/emulator/libemulator.so \ + artifacts + test $? -eq 0 || { echo "Can't copy final binaries"; exit 1; } + cp -R crypto/smartcont artifacts + cp -R crypto/fift/lib artifacts + chmod -R +x artifacts/* +fi + +if [ "$with_tests" = true ]; then + cd build +# ctest --output-on-failure -E "test-catchain|test-actors|test-smartcont|test-adnl|test-validator-session-state|test-dht|test-rldp" + ctest --output-on-failure --timeout 1800 +fi diff --git a/assembly/native/build-ubuntu-portable-libs.sh b/assembly/native/build-ubuntu-portable-libs.sh new file mode 100644 index 000000000..2f0a1ba4d --- /dev/null +++ b/assembly/native/build-ubuntu-portable-libs.sh @@ -0,0 +1,132 @@ +#/bin/bash + +#sudo apt-get update +#sudo apt-get install -y build-essential git cmake ninja-build automake libtool texinfo autoconf libc++-dev libc++abi-dev + +with_artifacts=false + +while getopts 'ta' flag; do + case "${flag}" in + a) with_artifacts=true ;; + *) break + ;; + esac +done + +if [ ! -d "build" ]; then + mkdir build + cd build +else + cd build + rm -rf .ninja* CMakeCache.txt +fi + +export CC=$(which clang) +export CXX=$(which clang++) +export CCACHE_DISABLE=1 + +if [ ! -d "lz4" ]; then +git clone https://github.com/lz4/lz4.git +cd lz4 +lz4Path=`pwd` +git checkout v1.9.4 +CFLAGS="-fPIC" make -j12 +test $? -eq 0 || { echo "Can't compile lz4"; exit 1; } +cd .. +# ./lib/liblz4.a +# ./lib +else + lz4Path=$(pwd)/lz4 + echo "Using compiled lz4" +fi + +if [ ! -d "libsodium" ]; then + export LIBSODIUM_FULL_BUILD=1 + git clone https://github.com/jedisct1/libsodium.git + cd libsodium + sodiumPath=`pwd` + git checkout 1.0.18 + ./autogen.sh + ./configure --with-pic --enable-static + make -j12 + test $? -eq 0 || { echo "Can't compile libsodium"; exit 1; } + cd .. +else + sodiumPath=$(pwd)/libsodium + echo "Using compiled libsodium" +fi + +if [ ! -d "openssl_3" ]; then + git clone https://github.com/openssl/openssl openssl_3 + cd openssl_3 + opensslPath=`pwd` + git checkout openssl-3.1.4 + ./config + make build_libs -j12 + test $? -eq 0 || { echo "Can't compile openssl_3"; exit 1; } + cd .. +else + opensslPath=$(pwd)/openssl_3 + echo "Using compiled openssl_3" +fi + +if [ ! -d "zlib" ]; then + git clone https://github.com/madler/zlib.git + cd zlib + zlibPath=`pwd` + ./configure --static + make -j12 + test $? -eq 0 || { echo "Can't compile zlib"; exit 1; } + cd .. +else + zlibPath=$(pwd)/zlib + echo "Using compiled zlib" +fi + +if [ ! -d "libmicrohttpd" ]; then + git clone https://git.gnunet.org/libmicrohttpd.git + cd libmicrohttpd + libmicrohttpdPath=`pwd` + ./autogen.sh + ./configure --enable-static --disable-tests --disable-benchmark --disable-shared --disable-https --with-pic + make -j12 + test $? -eq 0 || { echo "Can't compile libmicrohttpd"; exit 1; } + cd .. +else + libmicrohttpdPath=$(pwd)/libmicrohttpd + echo "Using compiled libmicrohttpd" +fi + +cmake -GNinja .. \ +-DPORTABLE=1 \ +-DCMAKE_BUILD_TYPE=Release \ +-DOPENSSL_FOUND=1 \ +-DOPENSSL_INCLUDE_DIR=$opensslPath/include \ +-DOPENSSL_CRYPTO_LIBRARY=$opensslPath/libcrypto.a \ +-DZLIB_FOUND=1 \ +-DZLIB_INCLUDE_DIR=$zlibPath \ +-DZLIB_LIBRARIES=$zlibPath/libz.a \ +-DSODIUM_FOUND=1 \ +-DSODIUM_INCLUDE_DIR=$sodiumPath/src/libsodium/include \ +-DSODIUM_LIBRARY_RELEASE=$sodiumPath/src/libsodium/.libs/libsodium.a \ +-DMHD_FOUND=1 \ +-DMHD_INCLUDE_DIR=$libmicrohttpdPath/src/include \ +-DMHD_LIBRARY=$libmicrohttpdPath/src/microhttpd/.libs/libmicrohttpd.a \ +-DLZ4_FOUND=1 \ +-DLZ4_INCLUDE_DIRS=$lz4Path/lib \ +-DLZ4_LIBRARIES=$lz4Path/lib/liblz4.a + + + +test $? -eq 0 || { echo "Can't configure ton"; exit 1; } + +ninja tonlibjson emulator +test $? -eq 0 || { echo "Can't compile ton"; exit 1; } + +cd .. + +mkdir artifacts +mv build/tonlib/libtonlibjson.so.0.5 build/tonlib/libtonlibjson.so +cp build/tonlib/libtonlibjson.so \ + build/emulator/libemulator.so \ + artifacts diff --git a/assembly/native/build-ubuntu-portable.sh b/assembly/native/build-ubuntu-portable.sh index 389c114e6..16e77ac8d 100644 --- a/assembly/native/build-ubuntu-portable.sh +++ b/assembly/native/build-ubuntu-portable.sh @@ -1,7 +1,7 @@ #/bin/bash #sudo apt-get update -#sudo apt-get install -y build-essential git cmake ninja-build automake libtool texinfo autoconf +#sudo apt-get install -y build-essential git cmake ninja-build automake libtool texinfo autoconf libc++-dev libc++abi-dev with_tests=false with_artifacts=false @@ -24,8 +24,8 @@ else rm -rf .ninja* CMakeCache.txt fi -export CC=$(which clang-16) -export CXX=$(which clang++-16) +export CC=$(which clang) +export CXX=$(which clang++) export CCACHE_DISABLE=1 if [ ! -d "lz4" ]; then @@ -33,7 +33,7 @@ git clone https://github.com/lz4/lz4.git cd lz4 lz4Path=`pwd` git checkout v1.9.4 -make -j12 +CFLAGS="-fPIC" make -j12 test $? -eq 0 || { echo "Can't compile lz4"; exit 1; } cd .. # ./lib/liblz4.a diff --git a/emulator/CMakeLists.txt b/emulator/CMakeLists.txt index 61de96d38..66d8309a5 100644 --- a/emulator/CMakeLists.txt +++ b/emulator/CMakeLists.txt @@ -35,7 +35,12 @@ else() add_library(emulator STATIC ${EMULATOR_SOURCE} ${EMULATOR_HEADERS}) endif() -target_link_libraries(emulator PUBLIC emulator_static git) +if (PORTABLE AND NOT APPLE) + target_link_libraries(emulator PUBLIC emulator_static git -static-libgcc -static-libstdc++) +else() + target_link_libraries(emulator PUBLIC emulator_static git) +endif() + generate_export_header(emulator EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/emulator_export.h) target_include_directories(emulator PUBLIC $ diff --git a/tonlib/CMakeLists.txt b/tonlib/CMakeLists.txt index 0855012cc..9a56e5119 100644 --- a/tonlib/CMakeLists.txt +++ b/tonlib/CMakeLists.txt @@ -98,7 +98,12 @@ else() add_library(tonlibjson STATIC ${TONLIB_JSON_SOURCE} ${TONLIB_JSON_HEADERS}) endif() -target_link_libraries(tonlibjson PRIVATE tonlibjson_private) +if (PORTABLE AND NOT APPLE) + target_link_libraries(tonlibjson PRIVATE tonlibjson_private -static-libgcc -static-libstdc++) +else() + target_link_libraries(tonlibjson PRIVATE tonlibjson_private) +endif() + generate_export_header(tonlibjson EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/tonlib/tonlibjson_export.h) if (!BUILD_SHARED_LIBS) target_compile_definitions(tonlibjson PUBLIC TONLIBJSON_STATIC_DEFINE) From 0f6cf13d45ce45a6ee011caa81020d542ff1b833 Mon Sep 17 00:00:00 2001 From: Ivan Kalinin Date: Fri, 24 Jan 2025 15:41:43 +0100 Subject: [PATCH 20/38] fix(vm): fix saving `ret` on deep jump (#1487) --- crypto/vm/vm.cpp | 9 +++++++-- crypto/vm/vm.h | 9 +++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/crypto/vm/vm.cpp b/crypto/vm/vm.cpp index 77d5d8f80..3c1118c60 100644 --- a/crypto/vm/vm.cpp +++ b/crypto/vm/vm.cpp @@ -247,6 +247,11 @@ int VmState::jump(Ref cont) { // general jump to continuation cont int VmState::jump(Ref cont, int pass_args) { + cont = adjust_jump_cont(std::move(cont), pass_args); + return jump_to(std::move(cont)); +} + +Ref VmState::adjust_jump_cont(Ref cont, int pass_args) { const ControlData* cont_data = cont->get_cdata(); if (cont_data) { // first do the checks @@ -287,7 +292,7 @@ int VmState::jump(Ref cont, int pass_args) { consume_stack_gas(copy); } } - return jump_to(std::move(cont)); + return cont; } else { // have no continuation data, situation is somewhat simpler if (pass_args >= 0) { @@ -299,7 +304,7 @@ int VmState::jump(Ref cont, int pass_args) { consume_stack_gas(pass_args); } } - return jump_to(std::move(cont)); + return cont; } } diff --git a/crypto/vm/vm.h b/crypto/vm/vm.h index 04c5e576c..7aaf1e911 100644 --- a/crypto/vm/vm.h +++ b/crypto/vm/vm.h @@ -347,6 +347,7 @@ class VmState final : public VmStateInterface { int call(Ref cont, int pass_args, int ret_args = -1); int jump(Ref cont); int jump(Ref cont, int pass_args); + Ref adjust_jump_cont(Ref cont, int pass_args); int ret(); int ret(int ret_args); int ret_alt(); @@ -374,6 +375,14 @@ class VmState final : public VmStateInterface { if (cnt > free_nested_cont_jump && global_version >= 9) { consume_gas(1); } + + if (cont.not_null()) { + const ControlData* cont_data = cont->get_cdata(); + if (cont_data && (cont_data->stack.not_null() || cont_data->nargs >= 0)) { + // if cont has non-empty stack or expects fixed number of arguments, jump is not simple + cont = adjust_jump_cont(std::move(cont), -1); + } + } } return res; } From da5644e758ff5f0bff504636dd20e1f8f6e257d6 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Fri, 24 Jan 2025 14:48:05 +0000 Subject: [PATCH 21/38] Enable VmState::jump_to bugfix in version 9 (#1491) --- crypto/block/transaction.cpp | 2 +- crypto/vm/vm.h | 3 +-- doc/GlobalVersions.md | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index 92e20fb0b..63e9065b6 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -1156,7 +1156,7 @@ namespace transaction { * It is activated by setting global version to 5 in ConfigParam 8. * This config change also activates new behavior for special accounts in masterchain. * - * In Augost 2024 it was decided to unlock other old highload wallets that got into the same situation. + * In August 2024 it was decided to unlock other old highload wallets that got into the same situation. * See https://t.me/tondev_news/129 * It is activated by setting global version to 9. * diff --git a/crypto/vm/vm.h b/crypto/vm/vm.h index 7aaf1e911..a171ef27e 100644 --- a/crypto/vm/vm.h +++ b/crypto/vm/vm.h @@ -375,8 +375,7 @@ class VmState final : public VmStateInterface { if (cnt > free_nested_cont_jump && global_version >= 9) { consume_gas(1); } - - if (cont.not_null()) { + if (cont.not_null() && global_version >= 9) { const ControlData* cont_data = cont->get_cdata(); if (cont_data && (cont_data->stack.not_null() || cont_data->nargs >= 0)) { // if cont has non-empty stack or expects fixed number of arguments, jump is not simple diff --git a/doc/GlobalVersions.md b/doc/GlobalVersions.md index 3849072f4..f4156ca07 100644 --- a/doc/GlobalVersions.md +++ b/doc/GlobalVersions.md @@ -133,4 +133,5 @@ Example: if the last masterchain block seqno is `19071` then the list contains b - Fix exception code in some TVM instructions: now `stk_und` has priority over other error codes. - `PFXDICTADD`, `PFXDICTSET`, `PFXDICTREPLACE`, `PFXDICTDEL`, `GETGASFEE`, `GETSTORAGEFEE`, `GETFORWARDFEE`, `GETORIGINALFWDFEE`, `GETGASFEESIMPLE`, `GETFORWARDFEESIMPLE`, `HASHEXT` - Now setting the contract code to a library cell does not consume additional gas on execution of the code. -- Temporary increase gas limit for some accounts (see [this post](https://t.me/tondev_news/129) for details, `override_gas_limit` in `transaction.cpp` for the list of accounts). \ No newline at end of file +- Temporary increase gas limit for some accounts (see [this post](https://t.me/tondev_news/129) for details, `override_gas_limit` in `transaction.cpp` for the list of accounts). +- Fix recursive jump to continuations with non-null control data. \ No newline at end of file From e7e57f8e6dac998039d1cfbd910320f3afa15959 Mon Sep 17 00:00:00 2001 From: Marat <98183742+dungeon-master-666@users.noreply.github.com> Date: Sun, 26 Jan 2025 14:39:05 +0100 Subject: [PATCH 22/38] add extra currencies support to emulator (#1494) --- emulator/emulator-emscripten.cpp | 31 ++++++++++++++++-- emulator/emulator-extern.cpp | 53 ++++++++++++++++++++++++++++++ emulator/emulator-extern.h | 8 +++++ emulator/test/emulator-tests.cpp | 55 ++++++++++++++++++++++++++++++++ emulator/tvm-emulator.hpp | 4 +++ 5 files changed, 149 insertions(+), 2 deletions(-) diff --git a/emulator/emulator-emscripten.cpp b/emulator/emulator-emscripten.cpp index 17639d280..e5d4e42d7 100644 --- a/emulator/emulator-emscripten.cpp +++ b/emulator/emulator-emscripten.cpp @@ -65,6 +65,7 @@ struct GetMethodParams { std::string address; uint32_t unixtime; uint64_t balance; + std::string extra_currencies; std::string rand_seed_hex; int64_t gas_limit; int method_id; @@ -108,6 +109,32 @@ td::Result decode_get_method_params(const char* json) { TRY_RESULT(balance, td::to_integer_safe(balance_field.get_string())); params.balance = balance; + TRY_RESULT(ec_field, td::get_json_object_field(obj, "extra_currencies", td::JsonValue::Type::Object, true)); + if (ec_field.type() != td::JsonValue::Type::Null) { + if (ec_field.type() != td::JsonValue::Type::Object) { + return td::Status::Error("EC must be of type Object"); + } + td::StringBuilder ec_builder; + auto ec_obj = ec_field.get_object(); + bool is_first = true; + for (auto &field_value : ec_obj) { + auto currency_id = field_value.first; + if (field_value.second.type() != td::JsonValue::Type::String) { + return td::Status::Error(PSLICE() << "EC amount must be of type String"); + } + auto amount = field_value.second.get_string(); + if (!is_first) { + ec_builder << " "; + is_first = false; + } + ec_builder << currency_id << "=" << amount; + } + if (ec_builder.is_error()) { + return td::Status::Error(PSLICE() << "Error building extra currencies string"); + } + params.extra_currencies = ec_builder.as_cslice().str(); + } + TRY_RESULT(rand_seed_str, td::get_json_object_string_field(obj, "rand_seed", false)); params.rand_seed_hex = rand_seed_str; @@ -228,8 +255,8 @@ const char *run_get_method(const char *params, const char* stack, const char* co if ((decoded_params.libs && !tvm_emulator_set_libraries(tvm, decoded_params.libs.value().c_str())) || !tvm_emulator_set_c7(tvm, decoded_params.address.c_str(), decoded_params.unixtime, decoded_params.balance, decoded_params.rand_seed_hex.c_str(), config) || - (decoded_params.prev_blocks_info && - !tvm_emulator_set_prev_blocks_info(tvm, decoded_params.prev_blocks_info.value().c_str())) || + (decoded_params.extra_currencies.size() > 0 && !tvm_emulator_set_extra_currencies(tvm, decoded_params.extra_currencies.c_str())) || + (decoded_params.prev_blocks_info && !tvm_emulator_set_prev_blocks_info(tvm, decoded_params.prev_blocks_info.value().c_str())) || (decoded_params.gas_limit > 0 && !tvm_emulator_set_gas_limit(tvm, decoded_params.gas_limit)) || !tvm_emulator_set_debug_enabled(tvm, decoded_params.debug_enabled)) { tvm_emulator_destroy(tvm); diff --git a/emulator/emulator-extern.cpp b/emulator/emulator-extern.cpp index 4e5f17bf7..eb5ff9f9e 100644 --- a/emulator/emulator-extern.cpp +++ b/emulator/emulator-extern.cpp @@ -496,6 +496,59 @@ bool tvm_emulator_set_c7(void *tvm_emulator, const char *address, uint32_t unixt return true; } +bool tvm_emulator_set_extra_currencies(void *tvm_emulator, const char *extra_currencies) { + auto emulator = static_cast(tvm_emulator); + vm::Dictionary dict{32}; + td::Slice extra_currencies_str{extra_currencies}; + while (true) { + auto next_space_pos = extra_currencies_str.find(' '); + auto currency_id_amount = next_space_pos == td::Slice::npos ? + extra_currencies_str.substr(0) : extra_currencies_str.substr(0, next_space_pos); + + if (!currency_id_amount.empty()) { + auto delim_pos = currency_id_amount.find('='); + if (delim_pos == td::Slice::npos) { + LOG(ERROR) << "Invalid extra currency format, missing '='"; + return false; + } + + auto currency_id_str = currency_id_amount.substr(0, delim_pos); + auto amount_str = currency_id_amount.substr(delim_pos + 1); + + auto currency_id = td::to_integer_safe(currency_id_str); + if (currency_id.is_error()) { + LOG(ERROR) << "Invalid extra currency id: " << currency_id_str; + return false; + } + auto amount = td::dec_string_to_int256(amount_str); + if (amount.is_null()) { + LOG(ERROR) << "Invalid extra currency amount: " << amount_str; + return false; + } + if (amount == 0) { + continue; + } + if (amount < 0) { + LOG(ERROR) << "Negative extra currency amount: " << amount_str; + return false; + } + + vm::CellBuilder cb; + block::tlb::t_VarUInteger_32.store_integer_value(cb, *amount); + if (!dict.set_builder(td::BitArray<32>(currency_id.ok()), cb, vm::DictionaryBase::SetMode::Add)) { + LOG(ERROR) << "Duplicate extra currency id"; + return false; + } + } + if (next_space_pos == td::Slice::npos) { + break; + } + extra_currencies_str.remove_prefix(next_space_pos + 1); + } + emulator->set_extra_currencies(std::move(dict).extract_root_cell()); + return true; +} + bool tvm_emulator_set_config_object(void* tvm_emulator, void* config) { auto emulator = static_cast(tvm_emulator); auto global_config = std::shared_ptr(static_cast(config), config_deleter); diff --git a/emulator/emulator-extern.h b/emulator/emulator-extern.h index e69a9cb0b..14879e1ec 100644 --- a/emulator/emulator-extern.h +++ b/emulator/emulator-extern.h @@ -182,6 +182,14 @@ EMULATOR_EXPORT bool tvm_emulator_set_libraries(void *tvm_emulator, const char * */ EMULATOR_EXPORT bool tvm_emulator_set_c7(void *tvm_emulator, const char *address, uint32_t unixtime, uint64_t balance, const char *rand_seed_hex, const char *config); +/** + * @brief Set extra currencies balance + * @param tvm_emulator Pointer to TVM emulator + * @param extra_currencies String with extra currencies balance in format "currency_id1=balance1 currency_id2=balance2 ..." + * @return true in case of success, false in case of error + */ +EMULATOR_EXPORT bool tvm_emulator_set_extra_currencies(void *tvm_emulator, const char *extra_currencies); + /** * @brief Set config for TVM emulator * @param tvm_emulator Pointer to TVM emulator diff --git a/emulator/test/emulator-tests.cpp b/emulator/test/emulator-tests.cpp index 24394b498..a0be447f5 100644 --- a/emulator/test/emulator-tests.cpp +++ b/emulator/test/emulator-tests.cpp @@ -400,3 +400,58 @@ TEST(Emulator, tvm_emulator) { CHECK(stack_res->depth() == 1); CHECK(stack_res.write().pop_int()->to_long() == init_data.seqno); } + +TEST(Emulator, tvm_emulator_extra_currencies) { + void *tvm_emulator = tvm_emulator_create("te6cckEBBAEAHgABFP8A9KQT9LzyyAsBAgFiAgMABtBfBAAJofpP8E8XmGlj", "te6cckEBAQEAAgAAAEysuc0=", 1); + std::string addr = "0:" + std::string(64, 'F'); + tvm_emulator_set_c7(tvm_emulator, addr.c_str(), 1337, 1000, std::string(64, 'F').c_str(), nullptr); + CHECK(tvm_emulator_set_extra_currencies(tvm_emulator, "100=20000 200=1")); + unsigned method_crc = td::crc16("get_balance"); + unsigned method_id = (method_crc & 0xffff) | 0x10000; + + auto stack = td::make_ref(); + vm::CellBuilder stack_cb; + CHECK(stack->serialize(stack_cb)); + auto stack_cell = stack_cb.finalize(); + auto stack_boc = td::base64_encode(std_boc_serialize(stack_cell).move_as_ok()); + + std::string tvm_res = tvm_emulator_run_get_method(tvm_emulator, method_id, stack_boc.c_str()); + + auto result_json = td::json_decode(td::MutableSlice(tvm_res)); + auto result = result_json.move_as_ok(); + auto& result_obj = result.get_object(); + + auto success_field = td::get_json_object_field(result_obj, "success", td::JsonValue::Type::Boolean, false); + auto success = success_field.move_as_ok().get_boolean(); + CHECK(success); + + auto stack_field = td::get_json_object_field(result_obj, "stack", td::JsonValue::Type::String, false); + auto stack_val = stack_field.move_as_ok(); + auto& stack_obj = stack_val.get_string(); + auto stack_res_boc = td::base64_decode(stack_obj); + auto stack_res_cell = vm::std_boc_deserialize(stack_res_boc.move_as_ok()); + td::Ref stack_res; + auto stack_res_cs = vm::load_cell_slice(stack_res_cell.move_as_ok()); + CHECK(vm::Stack::deserialize_to(stack_res_cs, stack_res)); + CHECK(stack_res->depth() == 1); + auto tuple = stack_res.write().pop_tuple(); + CHECK(tuple->size() == 2); + + auto ton_balance = tuple->at(0).as_int(); + CHECK(ton_balance == 1000); + + auto cell = tuple->at(1).as_cell(); + auto dict = vm::Dictionary{cell, 32}; + auto it = dict.begin(); + std::map ec_balance; + while (!it.eof()) { + auto id = td::BitArray<32>(it.cur_pos()).to_ulong(); + auto value_cs = it.cur_value(); + auto value = block::tlb::t_VarUInteger_32.as_integer(value_cs); + ec_balance[id] = value; + ++it; + } + CHECK(ec_balance.size() == 2); + CHECK(ec_balance[100] == 20000); + CHECK(ec_balance[200] == 1); +} diff --git a/emulator/tvm-emulator.hpp b/emulator/tvm-emulator.hpp index 413298c99..acc13627c 100644 --- a/emulator/tvm-emulator.hpp +++ b/emulator/tvm-emulator.hpp @@ -33,6 +33,10 @@ class TvmEmulator { } } + void set_extra_currencies(td::Ref extra_currencies) { + args_.set_extra_currencies(std::move(extra_currencies)); + } + void set_c7_raw(td::Ref c7) { args_.set_c7(std::move(c7)); } From 7d9ef6e0bfcd41143f2cea3327d199dcb1c806be Mon Sep 17 00:00:00 2001 From: neodix42 Date: Mon, 27 Jan 2025 11:18:51 +0400 Subject: [PATCH 23/38] Fix wasm artifacts (#1499) * put back emscripten 3.1.19 * add create-tolk-release.yml * filter out master branch only --- .../workflows/build-ton-wasm-emscripten.yml | 19 + .github/workflows/create-release.yml | 11 + .github/workflows/create-tolk-release.yml | 153 ++++ assembly/wasm/fift-func-wasm-build-ubuntu.sh | 4 +- assembly/wasm/intrinsics.fc | 61 ++ assembly/wasm/stdlib.fc | 681 ++++++++++++++++++ 6 files changed, 927 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/create-tolk-release.yml create mode 100644 assembly/wasm/intrinsics.fc create mode 100644 assembly/wasm/stdlib.fc diff --git a/.github/workflows/build-ton-wasm-emscripten.yml b/.github/workflows/build-ton-wasm-emscripten.yml index 2ac1a2245..bac0cf984 100644 --- a/.github/workflows/build-ton-wasm-emscripten.yml +++ b/.github/workflows/build-ton-wasm-emscripten.yml @@ -25,6 +25,25 @@ jobs: chmod +x fift-func-wasm-build-ubuntu.sh ./fift-func-wasm-build-ubuntu.sh -a + - name: Prepare test + run: | + cp assembly/wasm/*.fc . + git clone https://github.com/ton-community/func-js.git + cd func-js + npm install + npm run build + npm link + + - name: Test TON WASM artifacts + run: | + base64 -w 0 artifacts/funcfiftlib.wasm > artifacts/funcfiftlib.wasm.js + printf "module.exports = { FuncFiftLibWasm: '" | cat - artifacts/funcfiftlib.wasm.js > temp.txt && mv temp.txt artifacts/funcfiftlib.wasm.js + echo "'}" >> artifacts/funcfiftlib.wasm.js + cp artifacts/funcfiftlib.wasm.js func-js/node_modules/@ton-community/func-js-bin/dist/funcfiftlib.wasm.js + cp artifacts/funcfiftlib.js func-js/node_modules/@ton-community/func-js-bin/dist/funcfiftlib.js + npx func-js stdlib.fc intrinsics.fc --fift ./output.f + + - name: Upload artifacts uses: actions/upload-artifact@master with: diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 3063ce06e..1b86d045e 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -17,6 +17,7 @@ jobs: workflow: build-ton-linux-arm64-appimage.yml path: artifacts workflow_conclusion: success + branch: master skip_unpack: true - name: Download and unzip Linux arm64 artifacts @@ -25,6 +26,7 @@ jobs: workflow: build-ton-linux-arm64-appimage.yml path: artifacts workflow_conclusion: success + branch: master skip_unpack: false - name: Download Linux x86-64 artifacts @@ -33,6 +35,7 @@ jobs: workflow: build-ton-linux-x86-64-appimage.yml path: artifacts workflow_conclusion: success + branch: master skip_unpack: true - name: Download and unzip Linux x86-64 artifacts @@ -41,6 +44,7 @@ jobs: workflow: build-ton-linux-x86-64-appimage.yml path: artifacts workflow_conclusion: success + branch: master skip_unpack: false - name: Download Mac x86-64 artifacts @@ -49,6 +53,7 @@ jobs: workflow: build-ton-macos-13-x86-64-portable.yml path: artifacts workflow_conclusion: success + branch: master skip_unpack: true - name: Download Mac arm64 artifacts @@ -57,6 +62,7 @@ jobs: workflow: build-ton-macos-14-arm64-portable.yml path: artifacts workflow_conclusion: success + branch: master skip_unpack: true - name: Download and unzip Mac x86-64 artifacts @@ -73,6 +79,7 @@ jobs: workflow: build-ton-macos-14-arm64-portable.yml path: artifacts workflow_conclusion: success + branch: master skip_unpack: false - name: Download Windows artifacts @@ -81,6 +88,7 @@ jobs: workflow: ton-x86-64-windows.yml path: artifacts workflow_conclusion: success + branch: master skip_unpack: true - name: Download and unzip Windows artifacts @@ -89,6 +97,7 @@ jobs: workflow: ton-x86-64-windows.yml path: artifacts workflow_conclusion: success + branch: master skip_unpack: false - name: Download WASM artifacts @@ -97,6 +106,7 @@ jobs: workflow: build-ton-wasm-emscripten.yml path: artifacts workflow_conclusion: success + branch: master skip_unpack: true - name: Download Android Tonlib artifacts @@ -105,6 +115,7 @@ jobs: workflow: build-ton-linux-android-tonlib.yml path: artifacts workflow_conclusion: success + branch: master skip_unpack: true - name: Show all artifacts diff --git a/.github/workflows/create-tolk-release.yml b/.github/workflows/create-tolk-release.yml new file mode 100644 index 000000000..370f0d791 --- /dev/null +++ b/.github/workflows/create-tolk-release.yml @@ -0,0 +1,153 @@ +name: Create tolk release + +on: + workflow_dispatch: + inputs: + tag: + description: 'tolk release and tag name' + required: true + +permissions: write-all + +jobs: + create-release: + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v3 + + - name: Download and unzip Linux arm64 artifacts + uses: dawidd6/action-download-artifact@v6 + with: + workflow: build-ton-linux-arm64-appimage.yml + path: artifacts + workflow_conclusion: success + branch: master + skip_unpack: false + + - name: Download and unzip Linux x86-64 artifacts + uses: dawidd6/action-download-artifact@v6 + with: + workflow: build-ton-linux-x86-64-appimage.yml + path: artifacts + workflow_conclusion: success + branch: master + skip_unpack: false + + - name: Download and unzip Mac x86-64 artifacts + uses: dawidd6/action-download-artifact@v6 + with: + workflow: build-ton-macos-13-x86-64-portable.yml + path: artifacts + workflow_conclusion: success + branch: master + skip_unpack: false + + - name: Download and unzip arm64 artifacts + uses: dawidd6/action-download-artifact@v6 + with: + workflow: build-ton-macos-14-arm64-portable.yml + path: artifacts + workflow_conclusion: success + branch: master + skip_unpack: false + + - name: Download and unzip Windows artifacts + uses: dawidd6/action-download-artifact@v6 + with: + workflow: ton-x86-64-windows.yml + path: artifacts + workflow_conclusion: success + branch: master + skip_unpack: false + + - name: Download WASM artifacts + uses: dawidd6/action-download-artifact@v6 + with: + workflow: build-ton-wasm-emscripten.yml + path: artifacts + workflow_conclusion: success + branch: master + skip_unpack: true + + - name: Show all artifacts + run: | + tree artifacts + + + # create release + - name: Get registration token + id: getRegToken + run: | + curl -X POST -H \"Accept: application/vnd.github+json\" -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/ton-blockchain/ton/actions/runners/registration-token + + - name: Create release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ inputs.tag }} + release_name: ${{ inputs.tag }} + draft: false + prerelease: false + + # upload + + # win + + - name: Upload Windows 2019 single artifact - tolk + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-x86-64-windows/tolk.exe + asset_name: tolk.exe + tag: ${{ inputs.tag }} + + # mac x86-64 + + - name: Upload Mac x86-64 single artifact - tolk + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-x86_64-macos/tolk + asset_name: tolk-mac-x86-64 + tag: ${{ inputs.tag }} + + # mac arm64 + + - name: Upload Mac arm64 single artifact - tolk + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-arm64-macos/tolk + asset_name: tolk-mac-arm64 + tag: ${{ inputs.tag }} + + # linux x86-64 + + - name: Upload Linux x86-64 single artifact - tolk + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-x86_64-linux/tolk + asset_name: tolk-linux-x86_64 + tag: ${{ inputs.tag }} + + # linux arm64 + + - name: Upload Linux arm64 single artifact - tolk + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-arm64-linux/tolk + asset_name: tolk-linux-arm64 + tag: ${{ inputs.tag }} + + - name: Upload WASM artifacts + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: artifacts/ton-wasm.zip + asset_name: ton-wasm.zip + tag: ${{ inputs.tag }} diff --git a/assembly/wasm/fift-func-wasm-build-ubuntu.sh b/assembly/wasm/fift-func-wasm-build-ubuntu.sh index 8c0069f37..a463c02aa 100644 --- a/assembly/wasm/fift-func-wasm-build-ubuntu.sh +++ b/assembly/wasm/fift-func-wasm-build-ubuntu.sh @@ -71,8 +71,8 @@ echo fi cd emsdk -./emsdk install 3.1.40 -./emsdk activate 3.1.40 +./emsdk install 3.1.19 +./emsdk activate 3.1.19 EMSDK_DIR=`pwd` . $EMSDK_DIR/emsdk_env.sh diff --git a/assembly/wasm/intrinsics.fc b/assembly/wasm/intrinsics.fc new file mode 100644 index 000000000..14a4498d8 --- /dev/null +++ b/assembly/wasm/intrinsics.fc @@ -0,0 +1,61 @@ +#pragma allow-post-modification; +#pragma compute-asm-ltr; + +(slice, slice) __tact_load_address(slice cs) inline { + slice raw = cs~load_msg_addr(); + return (cs, raw); +} + +slice __gen_slice1 () asm """ + B{b5ee9c72410101010005000006abcdefe1e98884} B>boc boc boc boc boc tuple cons(X head, tuple tail) asm "CONS"; + +;;; Extracts the head and the tail of lisp-style list. +forall X -> (X, tuple) uncons(tuple list) asm "UNCONS"; + +;;; Extracts the tail and the head of lisp-style list. +forall X -> (tuple, X) list_next(tuple list) asm(-> 1 0) "UNCONS"; + +;;; Returns the head of lisp-style list. +forall X -> X car(tuple list) asm "CAR"; + +;;; Returns the tail of lisp-style list. +tuple cdr(tuple list) asm "CDR"; + +;;; Creates tuple with zero elements. +tuple empty_tuple() asm "NIL"; + +;;; Appends a value `x` to a `Tuple t = (x1, ..., xn)`, but only if the resulting `Tuple t' = (x1, ..., xn, x)` +;;; is of length at most 255. Otherwise throws a type check exception. +forall X -> tuple tpush(tuple t, X value) asm "TPUSH"; +forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH"; + +;;; Creates a tuple of length one with given argument as element. +forall X -> [X] single(X x) asm "SINGLE"; + +;;; Unpacks a tuple of length one +forall X -> X unsingle([X] t) asm "UNSINGLE"; + +;;; Creates a tuple of length two with given arguments as elements. +forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR"; + +;;; Unpacks a tuple of length two +forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR"; + +;;; Creates a tuple of length three with given arguments as elements. +forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE"; + +;;; Unpacks a tuple of length three +forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE"; + +;;; Creates a tuple of length four with given arguments as elements. +forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE"; + +;;; Unpacks a tuple of length four +forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE"; + +;;; Returns the first element of a tuple (with unknown element types). +forall X -> X first(tuple t) asm "FIRST"; + +;;; Returns the second element of a tuple (with unknown element types). +forall X -> X second(tuple t) asm "SECOND"; + +;;; Returns the third element of a tuple (with unknown element types). +forall X -> X third(tuple t) asm "THIRD"; + +;;; Returns the fourth element of a tuple (with unknown element types). +forall X -> X fourth(tuple t) asm "3 INDEX"; + +;;; Returns the first element of a pair tuple. +forall X, Y -> X pair_first([X, Y] p) asm "FIRST"; + +;;; Returns the second element of a pair tuple. +forall X, Y -> Y pair_second([X, Y] p) asm "SECOND"; + +;;; Returns the first element of a triple tuple. +forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST"; + +;;; Returns the second element of a triple tuple. +forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND"; + +;;; Returns the third element of a triple tuple. +forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD"; + + +;;; Push null element (casted to given type) +;;; By the TVM type `Null` FunC represents absence of a value of some atomic type. +;;; So `null` can actually have any atomic type. +forall X -> X null() asm "PUSHNULL"; + +;;; Moves a variable [x] to the top of the stack +forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP"; + + + +;;; Returns the current Unix time as an Integer +int now() asm "NOW"; + +;;; Returns the internal address of the current smart contract as a Slice with a `MsgAddressInt`. +;;; If necessary, it can be parsed further using primitives such as [parse_std_addr]. +slice my_address() asm "MYADDR"; + +;;; Returns the balance of the smart contract as a tuple consisting of an int +;;; (balance in nanotoncoins) and a `cell` +;;; (a dictionary with 32-bit keys representing the balance of "extra currencies") +;;; at the start of Computation Phase. +;;; Note that RAW primitives such as [send_raw_message] do not update this field. +[int, cell] get_balance() asm "BALANCE"; + +;;; Returns the logical time of the current transaction. +int cur_lt() asm "LTIME"; + +;;; Returns the starting logical time of the current block. +int block_lt() asm "BLOCKLT"; + +;;; Computes the representation hash of a `cell` [c] and returns it as a 256-bit unsigned integer `x`. +;;; Useful for signing and checking signatures of arbitrary entities represented by a tree of cells. +int cell_hash(cell c) asm "HASHCU"; + +;;; Computes the hash of a `slice s` and returns it as a 256-bit unsigned integer `x`. +;;; The result is the same as if an ordinary cell containing only data and references from `s` had been created +;;; and its hash computed by [cell_hash]. +int slice_hash(slice s) asm "HASHSU"; + +;;; Computes sha256 of the data bits of `slice` [s]. If the bit length of `s` is not divisible by eight, +;;; throws a cell underflow exception. The hash value is returned as a 256-bit unsigned integer `x`. +int string_hash(slice s) asm "SHA256U"; + +{- + # Signature checks +-} + +;;; Checks the Ed25519-`signature` of a `hash` (a 256-bit unsigned integer, usually computed as the hash of some data) +;;; using [public_key] (also represented by a 256-bit unsigned integer). +;;; The signature must contain at least 512 data bits; only the first 512 bits are used. +;;; The result is `−1` if the signature is valid, `0` otherwise. +;;; Note that `CHKSIGNU` creates a 256-bit slice with the hash and calls `CHKSIGNS`. +;;; That is, if [hash] is computed as the hash of some data, these data are hashed twice, +;;; the second hashing occurring inside `CHKSIGNS`. +int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU"; + +;;; Checks whether [signature] is a valid Ed25519-signature of the data portion of `slice data` using `public_key`, +;;; similarly to [check_signature]. +;;; If the bit length of [data] is not divisible by eight, throws a cell underflow exception. +;;; The verification of Ed25519 signatures is the standard one, +;;; with sha256 used to reduce [data] to the 256-bit number that is actually signed. +int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS"; + +{--- + # Computation of boc size + The primitives below may be useful for computing storage fees of user-provided data. +-} + +;;; Returns `(x, y, z, -1)` or `(null, null, null, 0)`. +;;; Recursively computes the count of distinct cells `x`, data bits `y`, and cell references `z` +;;; in the DAG rooted at `cell` [c], effectively returning the total storage used by this DAG taking into account +;;; the identification of equal cells. +;;; The values of `x`, `y`, and `z` are computed by a depth-first traversal of this DAG, +;;; with a hash table of visited cell hashes used to prevent visits of already-visited cells. +;;; The total count of visited cells `x` cannot exceed non-negative [max_cells]; +;;; otherwise the computation is aborted before visiting the `(max_cells + 1)`-st cell and +;;; a zero flag is returned to indicate failure. If [c] is `null`, returns `x = y = z = 0`. +(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE"; + +;;; Similar to [compute_data_size?], but accepting a `slice` [s] instead of a `cell`. +;;; The returned value of `x` does not take into account the cell that contains the `slice` [s] itself; +;;; however, the data bits and the cell references of [s] are accounted for in `y` and `z`. +(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE"; + +;;; A non-quiet version of [compute_data_size?] that throws a cell overflow exception (`8`) on failure. +(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; + +;;; A non-quiet version of [slice_compute_data_size?] that throws a cell overflow exception (8) on failure. +(int, int, int, int) slice_compute_data_size?(cell c, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; + +;;; Throws an exception with exit_code excno if cond is not 0 (commented since implemented in compilator) +;; () throw_if(int excno, int cond) impure asm "THROWARGIF"; + +{-- + # Debug primitives + Only works for local TVM execution with debug level verbosity +-} +;;; Dumps the stack (at most the top 255 values) and shows the total stack depth. +() dump_stack() impure asm "DUMPSTK"; + +{- + # Persistent storage save and load +-} + +;;; Returns the persistent contract storage cell. It can be parsed or modified with slice and builder primitives later. +cell get_data() asm "c4 PUSH"; + +;;; Sets `cell` [c] as persistent contract data. You can update persistent contract storage with this primitive. +() set_data(cell c) impure asm "c4 POP"; + +{- + # Continuation primitives +-} +;;; Usually `c3` has a continuation initialized by the whole code of the contract. It is used for function calls. +;;; The primitive returns the current value of `c3`. +cont get_c3() impure asm "c3 PUSH"; + +;;; Updates the current value of `c3`. Usually, it is used for updating smart contract code in run-time. +;;; Note that after execution of this primitive the current code +;;; (and the stack of recursive function calls) won't change, +;;; but any other function call will use a function from the new code. +() set_c3(cont c) impure asm "c3 POP"; + +;;; Transforms a `slice` [s] into a simple ordinary continuation `c`, with `c.code = s` and an empty stack and savelist. +cont bless(slice s) impure asm "BLESS"; + +{--- + # Gas related primitives +-} + +;;; Sets current gas limit `gl` to its maximal allowed value `gm`, and resets the gas credit `gc` to zero, +;;; decreasing the value of `gr` by `gc` in the process. +;;; In other words, the current smart contract agrees to buy some gas to finish the current transaction. +;;; This action is required to process external messages, which bring no value (hence no gas) with themselves. +;;; +;;; For more details check [accept_message effects](https://ton.org/docs/#/smart-contracts/accept). +() accept_message() impure asm "ACCEPT"; + +;;; Sets current gas limit `gl` to the minimum of limit and `gm`, and resets the gas credit `gc` to zero. +;;; If the gas consumed so far (including the present instruction) exceeds the resulting value of `gl`, +;;; an (unhandled) out of gas exception is thrown before setting new gas limits. +;;; Notice that [set_gas_limit] with an argument `limit ≥ 2^63 − 1` is equivalent to [accept_message]. +() set_gas_limit(int limit) impure asm "SETGASLIMIT"; + +;;; Commits the current state of registers `c4` (“persistent data”) and `c5` (“actions”) +;;; so that the current execution is considered “successful” with the saved values even if an exception +;;; in Computation Phase is thrown later. +() commit() impure asm "COMMIT"; + +;;; Not implemented +;;; Computes the amount of gas that can be bought for `amount` nanoTONs, +;;; and sets `gl` accordingly in the same way as [set_gas_limit]. +;;() buy_gas(int amount) impure asm "BUYGAS"; + +;;; Computes the minimum of two integers [x] and [y]. +int min(int x, int y) asm "MIN"; + +;;; Computes the maximum of two integers [x] and [y]. +int max(int x, int y) asm "MAX"; + +;;; Sorts two integers. +(int, int) minmax(int x, int y) asm "MINMAX"; + +;;; Computes the absolute value of an integer [x]. +int abs(int x) asm "ABS"; + +{- + # Slice primitives + + It is said that a primitive _loads_ some data, + if it returns the data and the remainder of the slice + (so it can also be used as [modifying method](https://ton.org/docs/#/func/statements?id=modifying-methods)). + + It is said that a primitive _preloads_ some data, if it returns only the data + (it can be used as [non-modifying method](https://ton.org/docs/#/func/statements?id=non-modifying-methods)). + + Unless otherwise stated, loading and preloading primitives read the data from a prefix of the slice. +-} + + +;;; Converts a `cell` [c] into a `slice`. Notice that [c] must be either an ordinary cell, +;;; or an exotic cell (see [TVM.pdf](https://ton-blockchain.github.io/docs/tvm.pdf), 3.1.2) +;;; which is automatically loaded to yield an ordinary cell `c'`, converted into a `slice` afterwards. +slice begin_parse(cell c) asm "CTOS"; + +;;; Checks if [s] is empty. If not, throws an exception. +() end_parse(slice s) impure asm "ENDS"; + +;;; Loads the first reference from the slice. +(slice, cell) load_ref(slice s) asm(-> 1 0) "LDREF"; + +;;; Preloads the first reference from the slice. +cell preload_ref(slice s) asm "PLDREF"; + +{- Functions below are commented because are implemented on compilator level for optimisation -} + +;;; Loads a signed [len]-bit integer from a slice [s]. +;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX"; + +;;; Loads an unsigned [len]-bit integer from a slice [s]. +;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX"; + +;;; Preloads a signed [len]-bit integer from a slice [s]. +;; int preload_int(slice s, int len) asm "PLDIX"; + +;;; Preloads an unsigned [len]-bit integer from a slice [s]. +;; int preload_uint(slice s, int len) asm "PLDUX"; + +;;; Loads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate `slice s''`. +;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX"; + +;;; Preloads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate `slice s''`. +;; slice preload_bits(slice s, int len) asm "PLDSLICEX"; + +;;; Loads serialized amount of TonCoins (any unsigned integer up to `2^120 - 1`). +(slice, int) load_grams(slice s) asm(-> 1 0) "LDGRAMS"; +(slice, int) load_coins(slice s) asm(-> 1 0) "LDVARUINT16"; + +(slice, int) load_varint16(slice s) asm(-> 1 0) "LDVARINT16"; +(slice, int) load_varint32(slice s) asm(-> 1 0) "LDVARINT32"; +(slice, int) load_varuint16(slice s) asm(-> 1 0) "LDVARUINT16"; +(slice, int) load_varuint32(slice s) asm(-> 1 0) "LDVARUINT32"; + +;;; Returns all but the first `0 ≤ len ≤ 1023` bits of `slice` [s]. +slice skip_bits(slice s, int len) asm "SDSKIPFIRST"; +(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST"; + +;;; Returns the first `0 ≤ len ≤ 1023` bits of `slice` [s]. +slice first_bits(slice s, int len) asm "SDCUTFIRST"; + +;;; Returns all but the last `0 ≤ len ≤ 1023` bits of `slice` [s]. +slice skip_last_bits(slice s, int len) asm "SDSKIPLAST"; +(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST"; + +;;; Returns the last `0 ≤ len ≤ 1023` bits of `slice` [s]. +slice slice_last(slice s, int len) asm "SDCUTLAST"; + +;;; Loads a dictionary `D` (HashMapE) from `slice` [s]. +;;; (returns `null` if `nothing` constructor is used). +(slice, cell) load_dict(slice s) asm(-> 1 0) "LDDICT"; + +;;; Preloads a dictionary `D` from `slice` [s]. +cell preload_dict(slice s) asm "PLDDICT"; + +;;; Loads a dictionary as [load_dict], but returns only the remainder of the slice. +slice skip_dict(slice s) asm "SKIPDICT"; +(slice, ()) ~skip_dict(slice s) asm "SKIPDICT"; + +;;; Loads (Maybe ^Cell) from `slice` [s]. +;;; In other words loads 1 bit and if it is true +;;; loads first ref and return it with slice remainder +;;; otherwise returns `null` and slice remainder +(slice, cell) load_maybe_ref(slice s) asm(-> 1 0) "LDOPTREF"; + +;;; Preloads (Maybe ^Cell) from `slice` [s]. +cell preload_maybe_ref(slice s) asm "PLDOPTREF"; + + +;;; Returns the depth of `cell` [c]. +;;; If [c] has no references, then return `0`; +;;; otherwise the returned value is one plus the maximum of depths of cells referred to from [c]. +;;; If [c] is a `null` instead of a cell, returns zero. +int cell_depth(cell c) asm "CDEPTH"; + + +{- + # Slice size primitives +-} + +;;; Returns the number of references in `slice` [s]. +int slice_refs(slice s) asm "SREFS"; + +;;; Returns the number of data bits in `slice` [s]. +int slice_bits(slice s) asm "SBITS"; + +;;; Returns both the number of data bits and the number of references in `slice` [s]. +(int, int) slice_bits_refs(slice s) asm "SBITREFS"; + +;;; Checks whether a `slice` [s] is empty (i.e., contains no bits of data and no cell references). +int slice_empty?(slice s) asm "SEMPTY"; + +;;; Checks whether `slice` [s] has no bits of data. +int slice_data_empty?(slice s) asm "SDEMPTY"; + +;;; Checks whether `slice` [s] has no references. +int slice_refs_empty?(slice s) asm "SREMPTY"; + +;;; Returns the depth of `slice` [s]. +;;; If [s] has no references, then returns `0`; +;;; otherwise the returned value is one plus the maximum of depths of cells referred to from [s]. +int slice_depth(slice s) asm "SDEPTH"; + +{- + # Builder size primitives +-} + +;;; Returns the number of cell references already stored in `builder` [b] +int builder_refs(builder b) asm "BREFS"; + +;;; Returns the number of data bits already stored in `builder` [b]. +int builder_bits(builder b) asm "BBITS"; + +;;; Returns the depth of `builder` [b]. +;;; If no cell references are stored in [b], then returns 0; +;;; otherwise the returned value is one plus the maximum of depths of cells referred to from [b]. +int builder_depth(builder b) asm "BDEPTH"; + +{- + # Builder primitives + It is said that a primitive _stores_ a value `x` into a builder `b` + if it returns a modified version of the builder `b'` with the value `x` stored at the end of it. + It can be used as [non-modifying method](https://ton.org/docs/#/func/statements?id=non-modifying-methods). + + All the primitives below first check whether there is enough space in the `builder`, + and only then check the range of the value being serialized. +-} + +;;; Creates a new empty `builder`. +builder begin_cell() asm "NEWC"; + +;;; Converts a `builder` into an ordinary `cell`. +cell end_cell(builder b) asm "ENDC"; + +;;; Stores a reference to `cell` [c] into `builder` [b]. +builder store_ref(builder b, cell c) asm(c b) "STREF"; + +;;; Stores an unsigned [len]-bit integer `x` into `b` for `0 ≤ len ≤ 256`. +;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX"; + +;;; Stores a signed [len]-bit integer `x` into `b` for` 0 ≤ len ≤ 257`. +;; builder store_int(builder b, int x, int len) asm(x b len) "STIX"; + + +;;; Stores `slice` [s] into `builder` [b] +builder store_slice(builder b, slice s) asm "STSLICER"; + +;;; Stores (serializes) an integer [x] in the range `0..2^120 − 1` into `builder` [b]. +;;; The serialization of [x] consists of a 4-bit unsigned big-endian integer `l`, +;;; which is the smallest integer `l ≥ 0`, such that `x < 2^8l`, +;;; followed by an `8l`-bit unsigned big-endian representation of [x]. +;;; If [x] does not belong to the supported range, a range check exception is thrown. +;;; +;;; Store amounts of TonCoins to the builder as VarUInteger 16 +builder store_grams(builder b, int x) asm "STGRAMS"; +builder store_coins(builder b, int x) asm "STVARUINT16"; + +builder store_varint16(builder b, int x) asm "STVARINT16"; +builder store_varint32(builder b, int x) asm "STVARINT32"; +builder store_varuint16(builder b, int x) asm "STVARUINT16"; +builder store_varuint32(builder b, int x) asm "STVARUINT32"; + +;;; Stores dictionary `D` represented by `cell` [c] or `null` into `builder` [b]. +;;; In other words, stores a `1`-bit and a reference to [c] if [c] is not `null` and `0`-bit otherwise. +builder store_dict(builder b, cell c) asm(c b) "STDICT"; + +;;; Stores (Maybe ^Cell) to builder: +;;; if cell is null store 1 zero bit +;;; otherwise store 1 true bit and ref to cell +builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF"; + + +{- + # Address manipulation primitives + The address manipulation primitives listed below serialize and deserialize values according to the following TL-B scheme: + ```TL-B + addr_none$00 = MsgAddressExt; + addr_extern$01 len:(## 8) external_address:(bits len) + = MsgAddressExt; + anycast_info$_ depth:(#<= 30) { depth >= 1 } + rewrite_pfx:(bits depth) = Anycast; + addr_std$10 anycast:(Maybe Anycast) + workchain_id:int8 address:bits256 = MsgAddressInt; + addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9) + workchain_id:int32 address:(bits addr_len) = MsgAddressInt; + _ _:MsgAddressInt = MsgAddress; + _ _:MsgAddressExt = MsgAddress; + + int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool + src:MsgAddress dest:MsgAddressInt + value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams + created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed; + ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt + created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed; + ``` + A deserialized `MsgAddress` is represented by a tuple `t` as follows: + + - `addr_none` is represented by `t = (0)`, + i.e., a tuple containing exactly one integer equal to zero. + - `addr_extern` is represented by `t = (1, s)`, + where slice `s` contains the field `external_address`. In other words, ` + t` is a pair (a tuple consisting of two entries), containing an integer equal to one and slice `s`. + - `addr_std` is represented by `t = (2, u, x, s)`, + where `u` is either a `null` (if `anycast` is absent) or a slice `s'` containing `rewrite_pfx` (if anycast is present). + Next, integer `x` is the `workchain_id`, and slice `s` contains the address. + - `addr_var` is represented by `t = (3, u, x, s)`, + where `u`, `x`, and `s` have the same meaning as for `addr_std`. +-} + +;;; Loads from slice [s] the only prefix that is a valid `MsgAddress`, +;;; and returns both this prefix `s'` and the remainder `s''` of [s] as slices. +(slice, slice) load_msg_addr(slice s) asm(-> 1 0) "LDMSGADDR"; + +;;; Decomposes slice [s] containing a valid `MsgAddress` into a `tuple t` with separate fields of this `MsgAddress`. +;;; If [s] is not a valid `MsgAddress`, a cell deserialization exception is thrown. +tuple parse_addr(slice s) asm "PARSEMSGADDR"; + +;;; Parses slice [s] containing a valid `MsgAddressInt` (usually a `msg_addr_std`), +;;; applies rewriting from the anycast (if present) to the same-length prefix of the address, +;;; and returns both the workchain and the 256-bit address as integers. +;;; If the address is not 256-bit, or if [s] is not a valid serialization of `MsgAddressInt`, +;;; throws a cell deserialization exception. +(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR"; + +;;; A variant of [parse_std_addr] that returns the (rewritten) address as a slice [s], +;;; even if it is not exactly 256 bit long (represented by a `msg_addr_var`). +(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR"; + +{- + # Dictionary primitives +-} + + +;;; Sets the value associated with [key_len]-bit key signed index in dictionary [dict] to [value] (cell), +;;; and returns the resulting dictionary. +cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; +(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; + +;;; Sets the value associated with [key_len]-bit key unsigned index in dictionary [dict] to [value] (cell), +;;; and returns the resulting dictionary. +cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF"; +(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF"; + +cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF"; +(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF" "NULLSWAPIFNOT"; +(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF" "NULLSWAPIFNOT"; +(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF"; +(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF"; +(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL"; +(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL"; +(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT"; +(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT"; +(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; +(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; +(cell, cell, int) idict_delete_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGETREF" "NULLSWAPIFNOT"; +(cell, cell, int) udict_delete_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~idict_delete_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~udict_delete_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGETREF" "NULLSWAPIFNOT"; +cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET"; +(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET"; +cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET"; +(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET"; +cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET"; +(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET"; +(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD"; +(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE"; +(cell, int) udict_replace_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUREPLACEREF"; +(cell, slice, int) udict_replaceget?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACEGET" "NULLSWAPIFNOT"; +(cell, cell, int) udict_replaceget_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUREPLACEGETREF" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~udict_replaceget?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACEGET" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~udict_replaceget_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUREPLACEGETREF" "NULLSWAPIFNOT"; +(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD"; +(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE"; +(cell, int) idict_replace_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTIREPLACEREF"; +(cell, slice, int) idict_replaceget?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACEGET" "NULLSWAPIFNOT"; +(cell, cell, int) idict_replaceget_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTIREPLACEGETREF" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~idict_replaceget?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACEGET" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~idict_replaceget_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTIREPLACEGETREF" "NULLSWAPIFNOT"; +cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB"; +(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB"; +cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB"; +(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB"; +cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB"; +(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB"; +(cell, int) dict_replace_builder?(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTREPLACEB"; +(cell, builder, int) dict_replaceget_builder?(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTREPLACEGETB" "NULLSWAPIFNOT"; +(cell, slice, int) dict_replaceget?(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTREPLACEGET" "NULLSWAPIFNOT"; +(cell, (builder, int)) ~dict_replaceget_builder?(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTREPLACEGETB" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~dict_replaceget?(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTREPLACEGET" "NULLSWAPIFNOT"; +(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB"; +(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB"; +(cell, builder, int) udict_replaceget_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEGETB" "NULLSWAPIFNOT"; +(cell, (builder, int)) ~udict_replaceget_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEGETB" "NULLSWAPIFNOT"; +(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB"; +(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB"; +(cell, builder, int) idict_replaceget_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEGETB" "NULLSWAPIFNOT"; +(cell, (builder, int)) ~idict_replaceget_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEGETB" "NULLSWAPIFNOT"; +(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; +(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; +(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; +(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; +(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; +(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; +(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; +(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2"; +(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2"; +(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2"; +(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2"; +(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2"; + +;;; Creates an empty dictionary, which is actually a null value. Equivalent to PUSHNULL +cell new_dict() asm "NEWDICT"; +;;; Checks whether a dictionary is empty. Equivalent to cell_null?. +int dict_empty?(cell c) asm "DICTEMPTY"; + + +{- Prefix dictionary primitives -} +(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2"; +(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET"; +(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL"; + +;;; Returns the value of the global configuration parameter with integer index `i` as a `cell` or `null` value. +cell config_param(int x) asm "CONFIGOPTPARAM"; +;;; Checks whether c is a null. Note, that FunC also has polymorphic null? built-in. +int cell_null?(cell c) asm "ISNULL"; + +;;; Creates an output action which would reserve exactly amount nanotoncoins (if mode = 0), at most amount nanotoncoins (if mode = 2), or all but amount nanotoncoins (if mode = 1 or mode = 3), from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying amount nanotoncoins (or b − amount nanotoncoins, where b is the remaining balance) to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. Bit +2 in mode means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. Bit +8 in mode means `amount <- -amount` before performing any further actions. Bit +4 in mode means that amount is increased by the original balance of the current account (before the compute phase), including all extra currencies, before performing any other checks and actions. Currently, amount must be a non-negative integer, and mode must be in the range 0..15. +() raw_reserve(int amount, int mode) impure asm "RAWRESERVE"; +;;; Similar to raw_reserve, but also accepts a dictionary extra_amount (represented by a cell or null) with extra currencies. In this way currencies other than TonCoin can be reserved. +() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX"; +;;; Sends a raw message contained in msg, which should contain a correctly serialized object Message X, with the only exception that the source address is allowed to have dummy value addr_none (to be automatically replaced with the current smart contract address), and ihr_fee, fwd_fee, created_lt and created_at fields can have arbitrary values (to be rewritten with correct values during the action phase of the current transaction). Integer parameter mode contains the flags. Currently mode = 0 is used for ordinary messages; mode = 128 is used for messages that are to carry all the remaining balance of the current smart contract (instead of the value originally indicated in the message); mode = 64 is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message (if bit 0 is not set, the gas fees are deducted from this amount); mode' = mode + 1 means that the sender wants to pay transfer fees separately; mode' = mode + 2 means that any errors arising while processing this message during the action phase should be ignored. Finally, mode' = mode + 32 means that the current account must be destroyed if its resulting balance is zero. This flag is usually employed together with +128. +() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG"; +;;; Creates an output action that would change this smart contract code to that given by cell new_code. Notice that this change will take effect only after the successful termination of the current run of the smart contract +() set_code(cell new_code) impure asm "SETCODE"; + +;;; Generates a new pseudo-random unsigned 256-bit integer x. The algorithm is as follows: if r is the old value of the random seed, considered as a 32-byte array (by constructing the big-endian representation of an unsigned 256-bit integer), then its sha512(r) is computed; the first 32 bytes of this hash are stored as the new value r' of the random seed, and the remaining 32 bytes are returned as the next random value x. +int random() impure asm "RANDU256"; +;;; Generates a new pseudo-random integer z in the range 0..range−1 (or range..−1, if range < 0). More precisely, an unsigned random value x is generated as in random; then z := x * range / 2^256 is computed. +int rand(int range) impure asm "RAND"; +;;; Returns the current random seed as an unsigned 256-bit Integer. +int get_seed() impure asm "RANDSEED"; +;;; Sets the random seed to unsigned 256-bit seed. +() set_seed(int x) impure asm "SETRAND"; +;;; Mixes unsigned 256-bit integer x into the random seed r by setting the random seed to sha256 of the concatenation of two 32-byte strings: the first with the big-endian representation of the old seed r, and the second with the big-endian representation of x. +() randomize(int x) impure asm "ADDRAND"; +;;; Equivalent to randomize(cur_lt());. +() randomize_lt() impure asm "LTIME" "ADDRAND"; + +;;; Checks whether the data parts of two slices coinside +int equal_slices_bits(slice a, slice b) asm "SDEQ"; +;;; Checks whether b is a null. Note, that FunC also has polymorphic null? built-in. +int builder_null?(builder b) asm "ISNULL"; +;;; Concatenates two builders +builder store_builder(builder to, builder from) asm "STBR"; + +;; CUSTOM: + +;; TVM UPGRADE 2023-07 https://docs.ton.org/learn/tvm-instructions/tvm-upgrade-2023-07 +;; In mainnet since 20 Dec 2023 https://t.me/tonblockchain/226 + +;;; Retrieves code of smart-contract from c7 + +cell my_code() asm "MYCODE"; From 99b78f78d7d05f3e7971d976e9fdb16db3e1c3b2 Mon Sep 17 00:00:00 2001 From: Marat <98183742+dungeon-master-666@users.noreply.github.com> Date: Mon, 27 Jan 2025 08:20:20 +0100 Subject: [PATCH 24/38] build fix (#1495) --- emulator/emulator-emscripten.cpp | 2 +- emulator/emulator_export_list | 1 + emulator/test/emulator-tests.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/emulator/emulator-emscripten.cpp b/emulator/emulator-emscripten.cpp index e5d4e42d7..efb14eff9 100644 --- a/emulator/emulator-emscripten.cpp +++ b/emulator/emulator-emscripten.cpp @@ -115,7 +115,7 @@ td::Result decode_get_method_params(const char* json) { return td::Status::Error("EC must be of type Object"); } td::StringBuilder ec_builder; - auto ec_obj = ec_field.get_object(); + auto& ec_obj = ec_field.get_object(); bool is_first = true; for (auto &field_value : ec_obj) { auto currency_id = field_value.first; diff --git a/emulator/emulator_export_list b/emulator/emulator_export_list index feb653e2f..bd991cd73 100644 --- a/emulator/emulator_export_list +++ b/emulator/emulator_export_list @@ -17,6 +17,7 @@ _emulator_config_destroy _tvm_emulator_create _tvm_emulator_set_libraries _tvm_emulator_set_c7 +_tvm_emulator_set_extra_currencies _tvm_emulator_set_config_object _tvm_emulator_set_prev_blocks_info _tvm_emulator_set_gas_limit diff --git a/emulator/test/emulator-tests.cpp b/emulator/test/emulator-tests.cpp index a0be447f5..ae273ddfd 100644 --- a/emulator/test/emulator-tests.cpp +++ b/emulator/test/emulator-tests.cpp @@ -445,7 +445,7 @@ TEST(Emulator, tvm_emulator_extra_currencies) { auto it = dict.begin(); std::map ec_balance; while (!it.eof()) { - auto id = td::BitArray<32>(it.cur_pos()).to_ulong(); + auto id = static_cast(td::BitArray<32>(it.cur_pos()).to_ulong()); auto value_cs = it.cur_value(); auto value = block::tlb::t_VarUInteger_32.as_integer(value_cs); ec_balance[id] = value; From ed88f55a3daedef0c4bc607b6594c060a05bd0e4 Mon Sep 17 00:00:00 2001 From: tuminzee <58180803+tuminzee@users.noreply.github.com> Date: Mon, 27 Jan 2025 13:05:56 +0530 Subject: [PATCH 25/38] fix broken link (#1497) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d0aa8cb39..897ba809a 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,9 @@ Main TON monorepo, which includes the code of the node/validator, lite-client, t __The Open Network (TON)__ is a fast, secure, scalable blockchain focused on handling _millions of transactions per second_ (TPS) with the goal of reaching hundreds of millions of blockchain users. - To learn more about different aspects of TON blockchain and its underlying ecosystem check [documentation](https://ton.org/docs) - To run node, validator or lite-server check [Participate section](https://ton.org/docs/participate/nodes/run-node) -- To develop decentralised apps check [Tutorials](https://ton.org/docs/develop/smart-contracts/), [FunC docs](https://ton.org/docs/develop/func/overview) and [DApp tutorials](https://ton.org/docs/develop/dapps/) +- To develop decentralised apps check [Tutorials](https://docs.ton.org/v3/guidelines/smart-contracts/guidelines), [FunC docs](https://ton.org/docs/develop/func/overview) and [DApp tutorials](https://docs.ton.org/v3/guidelines/dapps/overview) - To work on TON check [wallets](https://ton.app/wallets), [explorers](https://ton.app/explorers), [DEXes](https://ton.app/dex) and [utilities](https://ton.app/utilities) -- To interact with TON check [APIs](https://ton.org/docs/develop/dapps/apis/) +- To interact with TON check [APIs](https://docs.ton.org/v3/guidelines/dapps/apis-sdks/overview) ## Updates flow From 2a02b547868a4eb2fa69f44d4197ec46e919dc8d Mon Sep 17 00:00:00 2001 From: Andrey Tvorozhkov Date: Mon, 27 Jan 2025 14:22:00 +0500 Subject: [PATCH 26/38] Fix `advance_ext` (#746) --- crypto/vm/cells/CellSlice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/vm/cells/CellSlice.cpp b/crypto/vm/cells/CellSlice.cpp index 466bcd8d1..4d8c3c5ad 100644 --- a/crypto/vm/cells/CellSlice.cpp +++ b/crypto/vm/cells/CellSlice.cpp @@ -264,7 +264,7 @@ bool CellSlice::advance_ext(unsigned bits, unsigned refs) { } bool CellSlice::advance_ext(unsigned bits_refs) { - return advance_ext(bits_refs >> 16, bits_refs & 0xffff); + return advance_ext(bits_refs & 0xffff, bits_refs >> 16); } // (PRIVATE) From 8ffa3dd9dcab9135fe14a02f712c315bdd3079ed Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Mon, 27 Jan 2025 09:55:00 +0000 Subject: [PATCH 27/38] Fix printing TLB NatWidth (#1501) --- crypto/tl/tlblib.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/tl/tlblib.cpp b/crypto/tl/tlblib.cpp index ee05d371a..05ea8e1c2 100644 --- a/crypto/tl/tlblib.cpp +++ b/crypto/tl/tlblib.cpp @@ -45,7 +45,7 @@ bool Bool::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const { } bool NatWidth::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const { - long long value = (long long)cs.fetch_ulong(32); + long long value = (long long)cs.fetch_ulong(n); return value >= 0 && pp.out_int(value); } From 6f1feb43d5941f964e40691be8185c747f385793 Mon Sep 17 00:00:00 2001 From: EmelyanenkoK Date: Mon, 27 Jan 2025 12:58:54 +0300 Subject: [PATCH 28/38] Update Changelogs --- Changelog.md | 14 ++++++++++++++ recent_changelog.md | 21 ++++++++++----------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/Changelog.md b/Changelog.md index fd513bc81..fa713e7e0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,17 @@ +## 2025.02 Update +1. Series of improvement/fixes for `Config8.version >= 9`, check [GlobalVersion.md](./doc/GlobalVersion.md) +2. Fix for better discovery of updated nodes' (validators') IPs: retry dht queries +3. Series of improvements for extra currency adoption: fixed c7 in rungetmethod, reserve modes +4. TVM Fix: saving ret on deep jump +5. A few fixes of tl-b schemes: crc computation, incorrect tag for merkle proofs, advance_ext, NatWidth print +6. Emulator improvements: fix setting libraries, extracurrency support +7. Increase of gas limit for unlocking highload-v2 wallets locked in the beginning of 2024 +8. Validator console improvement: dashed names, better shard formats + + +Besides the work of the core team, this update is based on the efforts of @dbaranovstonfi from StonFi(libraries in emulator), @Rexagon (ret on deep jumps), @tvorogme from DTon (`advance_ext`), Nan from Zellic (`stk_und` and JNI) + + ## 2024.12 Update 1. FunC 0.4.6: Fix in try/catch handling, fixing pure flag for functions stored in variables diff --git a/recent_changelog.md b/recent_changelog.md index cc877c2ce..0ec085832 100644 --- a/recent_changelog.md +++ b/recent_changelog.md @@ -1,13 +1,12 @@ -## 2024.12 Update +## 2025.02 Update +1. Series of improvement/fixes for `Config8.version >= 9`, check [GlobalVersion.md](./doc/GlobalVersion.md) +2. Fix for better discovery of updated nodes' (validators') IPs: retry dht queries +3. Series of improvements for extra currency adoption: fixed c7 in rungetmethod, reserve modes +4. TVM Fix: saving ret on deep jump +5. A few fixes of tl-b schemes: crc computation, incorrect tag for merkle proofs, advance_ext, NatWidth print +6. Emulator improvements: fix setting libraries, extracurrency support +7. Increase of gas limit for unlocking highload-v2 wallets locked in the beginning of 2024 +8. Validator console improvement: dashed names, better shard formats -1. FunC 0.4.6: Fix in try/catch handling, fixing pure flag for functions stored in variables -2. Merging parts of Accelerator: support of specific shard monitoring, archive/liteserver slice format, support for partial liteservers, proxy liteserver, on-demand neighbour queue loading -3. Fix of asynchronous cell loading -4. Various improvements: caching certificates checks, better block overloading detection, `_malloc` in emulator -5. Introduction of telemetry in overlays -6. Use non-null local-id for tonlib-LS interaction - mitigates MitM attack. -7. Adding `SECP256K1_XONLY_PUBKEY_TWEAK_ADD`, `SETCONTCTRMANY` instructions to TVM (activated by `Config8.version >= 9`) -8. Private keys export via validator-engine-console - required for better backups -9. Fix proof checking in tonlib, `hash` in `raw.Message` in tonlib_api -Besides the work of the core team, this update is based on the efforts of OtterSec and LayerZero (FunC), tg:@throwunless (FunC), Aviv Frenkel and Dima Kogan from Fordefi (LS MitM), @hacker-volodya (Tonlib), OKX team (async cell loading), @krigga (emulator) +Besides the work of the core team, this update is based on the efforts of @dbaranovstonfi from StonFi(libraries in emulator), @Rexagon (ret on deep jumps), @tvorogme from DTon (`advance_ext`), Nan from Zellic (`stk_und` and JNI) From 294db6922717b88689fae4797804f953008cdb19 Mon Sep 17 00:00:00 2001 From: EmelyanenkoK Date: Mon, 27 Jan 2025 14:33:52 +0300 Subject: [PATCH 29/38] Fix typos in changelog --- Changelog.md | 5 ++--- recent_changelog.md | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index fa713e7e0..34195f74e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,8 +1,8 @@ ## 2025.02 Update -1. Series of improvement/fixes for `Config8.version >= 9`, check [GlobalVersion.md](./doc/GlobalVersion.md) +1. Series of improvement/fixes for `Config8.version >= 9`, check [GlobalVersions.md](./doc/GlobalVersions.md) 2. Fix for better discovery of updated nodes' (validators') IPs: retry dht queries 3. Series of improvements for extra currency adoption: fixed c7 in rungetmethod, reserve modes -4. TVM Fix: saving ret on deep jump +4. TVM: Fix processing continuation control data on deep jump 5. A few fixes of tl-b schemes: crc computation, incorrect tag for merkle proofs, advance_ext, NatWidth print 6. Emulator improvements: fix setting libraries, extracurrency support 7. Increase of gas limit for unlocking highload-v2 wallets locked in the beginning of 2024 @@ -11,7 +11,6 @@ Besides the work of the core team, this update is based on the efforts of @dbaranovstonfi from StonFi(libraries in emulator), @Rexagon (ret on deep jumps), @tvorogme from DTon (`advance_ext`), Nan from Zellic (`stk_und` and JNI) - ## 2024.12 Update 1. FunC 0.4.6: Fix in try/catch handling, fixing pure flag for functions stored in variables diff --git a/recent_changelog.md b/recent_changelog.md index 0ec085832..dfa39aa67 100644 --- a/recent_changelog.md +++ b/recent_changelog.md @@ -1,8 +1,8 @@ ## 2025.02 Update -1. Series of improvement/fixes for `Config8.version >= 9`, check [GlobalVersion.md](./doc/GlobalVersion.md) +1. Series of improvement/fixes for `Config8.version >= 9`, check [GlobalVersions.md](./doc/GlobalVersions.md) 2. Fix for better discovery of updated nodes' (validators') IPs: retry dht queries 3. Series of improvements for extra currency adoption: fixed c7 in rungetmethod, reserve modes -4. TVM Fix: saving ret on deep jump +4. TVM: Fix processing continuation control data on deep jump 5. A few fixes of tl-b schemes: crc computation, incorrect tag for merkle proofs, advance_ext, NatWidth print 6. Emulator improvements: fix setting libraries, extracurrency support 7. Increase of gas limit for unlocking highload-v2 wallets locked in the beginning of 2024 From c720204199d1615f1cffa00db6d376830dc8603d Mon Sep 17 00:00:00 2001 From: Marat <98183742+dungeon-master-666@users.noreply.github.com> Date: Mon, 27 Jan 2025 12:34:21 +0100 Subject: [PATCH 30/38] Fix BUILD_SHARED_LIBS issue (#1496) --- CMakeLists.txt | 1 + emulator/CMakeLists.txt | 16 ++++++---------- tonlib/CMakeLists.txt | 12 +++++------- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index af2640367..cea3fc7ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,6 +84,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED TRUE) set(CMAKE_CXX_EXTENSIONS FALSE) #BEGIN internal +option(BUILD_SHARED_LIBS "Use \"ON\" to build shared libraries instead of static where it's not specified (not recommended)" OFF) option(USE_EMSCRIPTEN "Use \"ON\" for config building wasm." OFF) option(TON_ONLY_TONLIB "Use \"ON\" to build only tonlib." OFF) if (USE_EMSCRIPTEN) diff --git a/emulator/CMakeLists.txt b/emulator/CMakeLists.txt index 66d8309a5..a07995414 100644 --- a/emulator/CMakeLists.txt +++ b/emulator/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR) -option(BUILD_SHARED_LIBS "Use \"OFF\" for a static build." ON) - if (NOT OPENSSL_FOUND) find_package(OpenSSL REQUIRED) endif() @@ -11,11 +9,6 @@ set(EMULATOR_STATIC_SOURCE tvm-emulator.hpp ) -set(EMULATOR_HEADERS - transaction-emulator.h - emulator-extern.h -) - set(EMULATOR_SOURCE emulator-extern.cpp ) @@ -29,10 +22,10 @@ include(GenerateExportHeader) add_library(emulator_static STATIC ${EMULATOR_STATIC_SOURCE}) target_link_libraries(emulator_static PUBLIC ton_crypto smc-envelope) -if (NOT USE_EMSCRIPTEN AND BUILD_SHARED_LIBS) - add_library(emulator SHARED ${EMULATOR_SOURCE} ${EMULATOR_HEADERS}) +if (USE_EMSCRIPTEN) + add_library(emulator STATIC ${EMULATOR_SOURCE}) else() - add_library(emulator STATIC ${EMULATOR_SOURCE} ${EMULATOR_HEADERS}) + add_library(emulator SHARED ${EMULATOR_SOURCE}) endif() if (PORTABLE AND NOT APPLE) @@ -42,6 +35,9 @@ else() endif() generate_export_header(emulator EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/emulator_export.h) +if (USE_EMSCRIPTEN) + target_compile_definitions(emulator PUBLIC EMULATOR_STATIC_DEFINE) +endif() target_include_directories(emulator PUBLIC $ $) diff --git a/tonlib/CMakeLists.txt b/tonlib/CMakeLists.txt index 9a56e5119..eb5383614 100644 --- a/tonlib/CMakeLists.txt +++ b/tonlib/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR) -option(BUILD_SHARED_LIBS "Use \"OFF\" for a static build." ON) - if (NOT OPENSSL_FOUND) find_package(OpenSSL REQUIRED) endif() @@ -92,10 +90,10 @@ set(TONLIB_JSON_HEADERS tonlib/tonlib_client_json.h) set(TONLIB_JSON_SOURCE tonlib/tonlib_client_json.cpp) include(GenerateExportHeader) -if (NOT USE_EMSCRIPTEN AND BUILD_SHARED_LIBS) - add_library(tonlibjson SHARED ${TONLIB_JSON_SOURCE} ${TONLIB_JSON_HEADERS}) +if (USE_EMSCRIPTEN) + add_library(tonlibjson STATIC ${TONLIB_JSON_SOURCE}) else() - add_library(tonlibjson STATIC ${TONLIB_JSON_SOURCE} ${TONLIB_JSON_HEADERS}) + add_library(tonlibjson SHARED ${TONLIB_JSON_SOURCE}) endif() if (PORTABLE AND NOT APPLE) @@ -105,7 +103,7 @@ else() endif() generate_export_header(tonlibjson EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/tonlib/tonlibjson_export.h) -if (!BUILD_SHARED_LIBS) +if (USE_EMSCRIPTEN) target_compile_definitions(tonlibjson PUBLIC TONLIBJSON_STATIC_DEFINE) endif() target_include_directories(tonlibjson PUBLIC @@ -159,7 +157,7 @@ endif() install(FILES ${TONLIB_JSON_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/tonlib/tonlibjson_export.h DESTINATION include/tonlib/) -if (NOT USE_EMSCRIPTEN AND BUILD_SHARED_LIBS) +if (NOT USE_EMSCRIPTEN) install(EXPORT Tonlib FILE TonlibTargets.cmake NAMESPACE Tonlib:: From 989629a832bd37faa89dedb9b0db29b0e0edc168 Mon Sep 17 00:00:00 2001 From: tolk-vm Date: Wed, 15 Jan 2025 01:41:15 +0700 Subject: [PATCH 31/38] [Tolk] Compiler built-in `__expect_type()` for testing purposes Currently, tolk-tester can test various "output" of the compiler: pass input and check output, validate fif codegen, etc. But it can not test compiler internals and AST representation. I've added an ability to have special functions to check/expose internal compiler state. The first (and the only now) is: > __expect_type(some_expr, ""); Such a call has special treatment in a compilation process. Compilation fails if this expression doesn't have requested type. It's intended to be used in tests only. Not present in stdlib. --- tolk-tester/tests/a6.tolk | 3 + tolk-tester/tests/generics-1.tolk | 5 +- tolk-tester/tests/inference-tests.tolk | 94 ++++++++++++++++++++++++++ tolk/builtins.cpp | 7 ++ tolk/lexer.cpp | 11 ++- tolk/lexer.h | 1 + tolk/pipe-infer-types-and-calls.cpp | 21 ++++++ tolk/type-system.cpp | 11 +-- tolk/type-system.h | 1 + 9 files changed, 147 insertions(+), 7 deletions(-) create mode 100644 tolk-tester/tests/inference-tests.tolk diff --git a/tolk-tester/tests/a6.tolk b/tolk-tester/tests/a6.tolk index 32fd3364c..944945464 100644 --- a/tolk-tester/tests/a6.tolk +++ b/tolk-tester/tests/a6.tolk @@ -1,6 +1,9 @@ fun f(a: int, b: int, c: int, d: int, e: int, f: int): (int, int) { // solve a 2x2 linear equation var D: int = a*d - b*c;;;; var Dx: int = e*d-b*f ;;;; var Dy: int = a * f - e * c; + __expect_type(D, "int"); + __expect_type(D*D, "int"); + __expect_type(calc_phi, "() -> int"); return (Dx/D,Dy/D); };;;; diff --git a/tolk-tester/tests/generics-1.tolk b/tolk-tester/tests/generics-1.tolk index 0d872cc19..5a649a440 100644 --- a/tolk-tester/tests/generics-1.tolk +++ b/tolk-tester/tests/generics-1.tolk @@ -41,10 +41,12 @@ fun manyEq(a: T1, b: T2, c: T3): [T1, T2, T3] { @method_id(104) fun test104(f: int) { - return ( + var result = ( manyEq(1 ? 1 : 1, f ? 0 : null, !f ? getTwo() as int : null), manyEq((f ? null as int : eq2(2), beginCell().storeBool(true).endCell().beginParse().loadBool()), 0, eq4(f)) ); + __expect_type(result, "([int, int, int], [(int, bool), int, int])"); + return result; } fun calcSum(x: X, y: X) { return x + y; } @@ -68,6 +70,7 @@ fun abstractTransform(xToY: (X) -> Y, yToR: (((Y))) -> R, initialX: X): @method_id(106) fun test106() { var c = beginCell().storeInt(106, 32).endCell(); + __expect_type(calcYPlus1, "(int) -> int"); return [ abstractTransform(cellToSlice, calcLoad32, c), abstractTransform(calcYPlus1, calcYPlus1, 0), diff --git a/tolk-tester/tests/inference-tests.tolk b/tolk-tester/tests/inference-tests.tolk new file mode 100644 index 000000000..3d4515816 --- /dev/null +++ b/tolk-tester/tests/inference-tests.tolk @@ -0,0 +1,94 @@ +// the goal of this file is not only to @testcase results — +// but to check that this file compiles + +fun eq(value: X): X { return value; } + +fun test1(x: int, y: int) { + __expect_type(0, "int"); + __expect_type("0"c, "int"); + __expect_type(x, "int"); + __expect_type(x + y, "int"); + __expect_type(x * y, "int"); + __expect_type(x & y, "int"); + __expect_type(x << y, "int"); + __expect_type((((x))), "int"); + __expect_type(x = x, "int"); + __expect_type(x += x, "int"); + __expect_type(x &= x, "int"); + __expect_type(random() ? x : y, "int"); + __expect_type(eq(x), "int"); + __expect_type(eq(x), "int"); + __expect_type(eq(null), "int"); + __expect_type(x as int, "int"); + __expect_type(+x, "int"); + __expect_type(~x, "int"); + { + var x: slice = beginCell().endCell().beginParse(); + __expect_type(x, "slice"); + __expect_type(beginCell(), "builder"); + __expect_type(beginCell().endCell(), "cell"); + } +} + +fun test2(x: int, y: bool) { + __expect_type(!x, "bool"); + __expect_type(x != x, "bool"); + __expect_type(x <= x, "bool"); + __expect_type(x <=> x, "bool"); + __expect_type(x <=> x, "bool"); + __expect_type(!random(), "bool"); + __expect_type(!!(x != null), "bool"); + __expect_type(x ? x != null : null == x, "bool"); + __expect_type(y & true, "bool"); + __expect_type(y ^= false, "bool"); + __expect_type(x && y, "bool"); + __expect_type(true && false && true, "bool"); + __expect_type(x || x, "bool"); + __expect_type(x || !x || (true & false), "bool"); +} + +fun test3() { + __expect_type(true as int, "int"); + __expect_type(!random() as int, "int"); +} + +fun test4(x: int) { + __expect_type((), "()"); + __expect_type((x, x), "(int, int)"); + __expect_type((x, (x, x), x), "(int, (int, int), int)"); +} + +fun test5(x: int) { + __expect_type([], "[]"); + __expect_type([x], "[int]"); + __expect_type([x, x >= 1], "[int, bool]"); + __expect_type([x, x >= 1, null as slice], "[int, bool, slice]"); + __expect_type((x, [x], [[x], x]), "(int, [int], [[int], int])"); + __expect_type(getMyOriginalBalanceWithExtraCurrencies(), "[int, cell]"); +} + +fun test6() { + var t = createEmptyTuple(); + __expect_type(t, "tuple"); + t.tuplePush(1); + __expect_type(t, "tuple"); +} + +fun test7() { + __expect_type(test3(), "void"); + __expect_type(test3, "() -> void"); + var cb = test1; + __expect_type(cb, "(int, int) -> void"); + var t = createEmptyTuple(); + __expect_type(beginCell().endCell, "(builder) -> cell"); + // __expect_type(eq<(int, slice)>, "(int, slice) -> (int, slice)"); +} + + +fun main() { + return 0; +} + +/** +@testcase | 0 | | 0 +*/ diff --git a/tolk/builtins.cpp b/tolk/builtins.cpp index d704ec4d3..73aef2d99 100644 --- a/tolk/builtins.cpp +++ b/tolk/builtins.cpp @@ -1255,6 +1255,13 @@ void define_builtins() { define_builtin_func("debugDumpStack", {}, Unit, nullptr, AsmOp::Custom("DUMPSTK", 0, 0), 0); + + // functions not presented in stdlib at all + // used in tolk-tester to check/expose internal compiler state + // each of them is handled in a special way, search by its name + define_builtin_func("__expect_type", {TypeDataUnknown::create(), Slice}, Unit, nullptr, + AsmOp::Nop(), + FunctionData::flagMarkedAsPure); } } // namespace tolk diff --git a/tolk/lexer.cpp b/tolk/lexer.cpp index 7e8c8fb2d..78ec991e1 100644 --- a/tolk/lexer.cpp +++ b/tolk/lexer.cpp @@ -555,6 +555,15 @@ Lexer::Lexer(const SrcFile* file) next(); } +Lexer::Lexer(std::string_view text) + : file(nullptr) + , p_start(text.data()) + , p_end(p_start + text.size()) + , p_next(p_start) + , location() { + next(); +} + void Lexer::next() { while (cur_token_idx == last_token_idx && !is_eof()) { update_location(); @@ -563,7 +572,7 @@ void Lexer::next() { } } if (is_eof()) { - add_token(tok_eof, file->text); + add_token(tok_eof, ""); } cur_token = tokens_circularbuf[++cur_token_idx & 7]; } diff --git a/tolk/lexer.h b/tolk/lexer.h index 81d579db0..9dbfe3b61 100644 --- a/tolk/lexer.h +++ b/tolk/lexer.h @@ -173,6 +173,7 @@ class Lexer { }; explicit Lexer(const SrcFile* file); + explicit Lexer(std::string_view text); Lexer(const Lexer&) = delete; Lexer &operator=(const Lexer&) = delete; diff --git a/tolk/pipe-infer-types-and-calls.cpp b/tolk/pipe-infer-types-and-calls.cpp index d8a7d41be..011c83d75 100644 --- a/tolk/pipe-infer-types-and-calls.cpp +++ b/tolk/pipe-infer-types-and-calls.cpp @@ -240,6 +240,24 @@ class TypeInferringUnifyStrategy { TypePtr get_result() const { return unified_result; } }; +// handle __expect_type(expr, "type") call +// this is used in compiler tests +GNU_ATTRIBUTE_NOINLINE GNU_ATTRIBUTE_COLD +static void handle_possible_compiler_internal_call(const FunctionData* current_function, V v) { + const FunctionData* fun_ref = v->fun_maybe; + tolk_assert(fun_ref && fun_ref->is_builtin_function()); + static_cast(current_function); + + if (fun_ref->name == "__expect_type") { + tolk_assert(v->get_num_args() == 2); + TypePtr expected_type = parse_type_from_string(v->get_arg(1)->get_expr()->as()->str_val); + TypePtr expr_type = v->get_arg(0)->inferred_type; + if (expected_type != expr_type) { + v->error("__expect_type failed: expected " + to_string(expected_type) + ", got " + to_string(expr_type)); + } + } +} + /* * This class handles all types of AST vertices and traverses them, filling all AnyExprV::inferred_type. * Note, that it isn't derived from ASTVisitor, it has manual `switch` over all existing vertex types. @@ -974,6 +992,9 @@ class InferCheckTypesAndCallsAndFieldsVisitor final { TypePtr inferred_type = dot_obj && fun_ref->does_return_self() ? dot_obj->inferred_type : fun_ref->inferred_return_type; assign_inferred_type(v, inferred_type); assign_inferred_type(callee, fun_ref->inferred_full_type); + if (fun_ref->is_builtin_function() && fun_ref->name[0] == '_') { + handle_possible_compiler_internal_call(current_function, v); + } // note, that mutate params don't affect typing, they are handled when converting to IR } diff --git a/tolk/type-system.cpp b/tolk/type-system.cpp index b21bd0eeb..72419d8c1 100644 --- a/tolk/type-system.cpp +++ b/tolk/type-system.cpp @@ -651,11 +651,6 @@ static TypePtr parse_simple_type(Lexer& lex) { std::vector items = parse_nested_type_list(lex, tok_opbracket, "`[`", tok_clbracket, "`]` or `,`"); return TypeDataTypedTuple::create(std::move(items)); } - case tok_fun: { - lex.next(); - std::vector params_types = parse_nested_type_list_in_parenthesis(lex); - lex.expect(tok_arrow, "`->`"); - } default: lex.unexpected(""); } @@ -695,6 +690,12 @@ TypePtr parse_type_from_tokens(Lexer& lex) { return parse_type_expression(lex); } +// for internal usage only +TypePtr parse_type_from_string(std::string_view text) { + Lexer lex(text); + return parse_type_expression(lex); +} + std::ostream& operator<<(std::ostream& os, TypePtr type_data) { return os << (type_data ? type_data->as_human_readable() : "(nullptr-type)"); } diff --git a/tolk/type-system.h b/tolk/type-system.h index 13c0e4b09..0db427091 100644 --- a/tolk/type-system.h +++ b/tolk/type-system.h @@ -417,6 +417,7 @@ class TypeDataVoid final : public TypeData { class Lexer; TypePtr parse_type_from_tokens(Lexer& lex); +TypePtr parse_type_from_string(std::string_view text); void type_system_init(); From 565bc597356481d5de047625fa94ae1b66658539 Mon Sep 17 00:00:00 2001 From: tolk-vm Date: Wed, 18 Dec 2024 19:26:26 +0300 Subject: [PATCH 32/38] [Tolk] Refactor: get rid of split_vars, construct valid LET ops In FunC (and in Tolk before), tensor vars (actually occupying several stack slots) were represented as a single var in terms or IR vars (Ops): > var a = (1, 2); > LET (_i) = (_1, _2) Now, every tensor of N stack slots is represented as N IR vars. > LET (_i, _j) = (_1, _2) This will give an ability to control access to parts of a tensor when implementing `tensorVar.0` syntax. --- tolk-tester/tests/a10.tolk | 4 +- tolk-tester/tests/cells-slices.tolk | 8 +-- tolk-tester/tests/logical-operators.tolk | 6 +- tolk-tester/tests/mutate-methods.tolk | 6 +- tolk-tester/tests/null-keyword.tolk | 4 +- tolk/abscode.cpp | 89 +++++------------------- tolk/analyzer.cpp | 34 --------- tolk/generics-helpers.cpp | 6 +- tolk/pipe-ast-to-legacy.cpp | 84 +++++++++++++--------- tolk/pipe-check-rvalue-lvalue.cpp | 2 +- tolk/pipe-generate-fif-output.cpp | 5 -- tolk/pipe-refine-lvalue-for-mutate.cpp | 4 +- tolk/symtable.cpp | 4 +- tolk/symtable.h | 11 +-- tolk/tolk.h | 19 ++--- tolk/type-system.cpp | 25 ------- tolk/type-system.h | 8 --- 17 files changed, 101 insertions(+), 218 deletions(-) diff --git a/tolk-tester/tests/a10.tolk b/tolk-tester/tests/a10.tolk index 7301f1d50..af1042487 100644 --- a/tolk-tester/tests/a10.tolk +++ b/tolk-tester/tests/a10.tolk @@ -104,9 +104,9 @@ fun testStartBalanceCodegen2() { testDumpDontPolluteStack PROC:<{ ... DUMPSTK - x{6d79} PUSHSLICE // f s _9 + x{6d79} PUSHSLICE // f s _5 STRDUMP DROP - SBITS // f _11 + SBITS // f _6 }> """ diff --git a/tolk-tester/tests/cells-slices.tolk b/tolk-tester/tests/cells-slices.tolk index 6f316f2e6..0a2807e6b 100644 --- a/tolk-tester/tests/cells-slices.tolk +++ b/tolk-tester/tests/cells-slices.tolk @@ -220,11 +220,11 @@ Note, that since 'compute-asm-ltr' became on be default, chaining methods codege 1 PUSHINT // _0 _1=1 SWAP // _1=1 _0 32 STU // _0 - 2 PUSHINT // _0 _5=2 - SWAP // _5=2 _0 + 2 PUSHINT // _0 _4=2 + SWAP // _4=2 _0 32 STU // _0 - 3 PUSHINT // _0 _9=3 - SWAP // _9=3 _0 + 3 PUSHINT // _0 _7=3 + SWAP // _7=3 _0 32 STU // _0 }> """ diff --git a/tolk-tester/tests/logical-operators.tolk b/tolk-tester/tests/logical-operators.tolk index fb437bb34..9ef4858dc 100644 --- a/tolk-tester/tests/logical-operators.tolk +++ b/tolk-tester/tests/logical-operators.tolk @@ -332,15 +332,15 @@ These are moments of future optimizations. For now, it's more than enough. DUP // x x IFNOTJMP:<{ // x DROP // - 1 PUSHINT // _7=1 + 1 PUSHINT // _5=1 }> // x DUP // x x IFNOTJMP:<{ // x DROP // - 1 PUSHINT // _8=1 + 1 PUSHINT // _6=1 }> // x 100 THROWIFNOT - -4 PUSHINT // _12=-4 + -4 PUSHINT // _9=-4 }> """ diff --git a/tolk-tester/tests/mutate-methods.tolk b/tolk-tester/tests/mutate-methods.tolk index 816e4c8d2..0a5ca1915 100644 --- a/tolk-tester/tests/mutate-methods.tolk +++ b/tolk-tester/tests/mutate-methods.tolk @@ -307,7 +307,7 @@ fun main(){} ... incrementTwoInPlace CALLDICT // x y sum1 -ROT - 10 PUSHINT // sum1 x y _8=10 + 10 PUSHINT // sum1 x y _10=10 incrementTwoInPlace CALLDICT // sum1 x y sum2 s1 s3 s0 XCHG3 // x y sum1 sum2 }> @@ -317,8 +317,8 @@ fun main(){} """ load_next PROC:<{ // cs - 32 LDI // _3 cs - SWAP // cs _3 + 32 LDI // _4 cs + SWAP // cs _4 }> """ diff --git a/tolk-tester/tests/null-keyword.tolk b/tolk-tester/tests/null-keyword.tolk index 9ace99956..40e391c8b 100644 --- a/tolk-tester/tests/null-keyword.tolk +++ b/tolk-tester/tests/null-keyword.tolk @@ -113,7 +113,7 @@ fun main() { CONS // numbers UNCONS // h numbers DUP // h numbers numbers - CAR // h numbers _12 + CAR // h numbers _13 """ @fif_codegen @@ -133,7 +133,7 @@ fun main() { """ test7 PROC:<{ ... - LDOPTREF // b _18 _17 + LDOPTREF // b _8 _7 DROP // b c ISNULL // b _11 10 MULCONST // b _13 diff --git a/tolk/abscode.cpp b/tolk/abscode.cpp index 7bcb0f84f..25ddd10ef 100644 --- a/tolk/abscode.cpp +++ b/tolk/abscode.cpp @@ -31,16 +31,6 @@ void TmpVar::dump(std::ostream& os) const { os << " : " << v_type << " (width "; os << v_type->calc_width_on_stack(); 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; } @@ -51,7 +41,7 @@ void TmpVar::show(std::ostream& os, int omit_idx) const { return; } } - os << '_' << idx; + os << '_' << ir_idx; } std::ostream& operator<<(std::ostream& os, const TmpVar& var) { @@ -182,47 +172,6 @@ void VarDescrList::show(std::ostream& os) const { os << " ]\n"; } -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 << " ["; @@ -444,26 +393,22 @@ void CodeBlob::print(std::ostream& os, int flags) const { os << "-------- END ---------\n\n"; } -var_idx_t CodeBlob::create_var(TypePtr var_type, const LocalVarData* v_sym, SrcLocation location) { - vars.emplace_back(var_cnt, var_type, v_sym, location); - return var_cnt++; -} - -bool CodeBlob::import_params(FormalArgList&& arg_list) { - if (var_cnt || in_var_cnt) { - return false; - } - std::vector list; - for (const auto& par : arg_list) { - TypePtr arg_type; - const LocalVarData* arg_sym; - SrcLocation arg_loc; - std::tie(arg_type, arg_sym, arg_loc) = par; - list.push_back(create_var(arg_type, arg_sym, arg_loc)); - } - emplace_back(loc, Op::_Import, list); - in_var_cnt = var_cnt; - return true; +std::vector CodeBlob::create_var(TypePtr var_type, const LocalVarData* v_sym, SrcLocation loc) { + std::vector ir_idx; + ir_idx.reserve(var_type->calc_width_on_stack()); + if (const TypeDataTensor* t_tensor = var_type->try_as()) { + for (TypePtr item : t_tensor->items) { + std::vector nested = create_var(item, v_sym, loc); + ir_idx.insert(ir_idx.end(), nested.begin(), nested.end()); + } + } else if (var_type != TypeDataVoid::create()) { + tolk_assert(var_type->calc_width_on_stack() == 1); + vars.emplace_back(var_cnt, var_type, v_sym, loc); + ir_idx.emplace_back(var_cnt); + var_cnt++; + } + tolk_assert(static_cast(ir_idx.size()) == var_type->calc_width_on_stack()); + return ir_idx; } } // namespace tolk diff --git a/tolk/analyzer.cpp b/tolk/analyzer.cpp index 8539afdd2..9303bc832 100644 --- a/tolk/analyzer.cpp +++ b/tolk/analyzer.cpp @@ -26,40 +26,6 @@ namespace tolk { * */ -int CodeBlob::split_vars(bool strict) { - int n = var_cnt, changes = 0; - for (int j = 0; j < var_cnt; j++) { - TmpVar& var = vars[j]; - int width_j = var.v_type->calc_width_on_stack(); - if (strict && width_j < 0) { - throw ParseError{var.where, "variable does not have fixed width, cannot manipulate it"}; - } - if (width_j == 1) { - continue; - } - std::vector comp_types; - var.v_type->extract_components(comp_types); - tolk_assert(width_j <= 254 && n <= 0x7fff00); - tolk_assert((unsigned)width_j == comp_types.size()); - var.coord = ~((n << 8) + width_j); - for (int i = 0; i < width_j; i++) { - auto v = create_var(comp_types[i], vars[j].v_sym, vars[j].where); - tolk_assert(v == n + i); - tolk_assert(vars[v].idx == v); - vars[v].coord = ((int)j << 8) + i + 1; - } - n += width_j; - ++changes; - } - 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); diff --git a/tolk/generics-helpers.cpp b/tolk/generics-helpers.cpp index 3d353cc4e..7a2dd83ff 100644 --- a/tolk/generics-helpers.cpp +++ b/tolk/generics-helpers.cpp @@ -202,8 +202,8 @@ td::Result> deduce_substitutionTs_on_generic_func_call(cons try { GenericSubstitutionsDeduceForFunctionCall deducing(called_fun); for (const LocalVarData& param : called_fun->parameters) { - if (param.declared_type->has_genericT_inside() && param.idx < static_cast(arg_types.size())) { - deducing.consider_next_condition(param.declared_type, arg_types[param.idx]); + if (param.declared_type->has_genericT_inside() && param.param_idx < static_cast(arg_types.size())) { + deducing.consider_next_condition(param.declared_type, arg_types[param.param_idx]); } } int idx = deducing.get_first_not_deduced_idx(); @@ -233,7 +233,7 @@ const FunctionData* instantiate_generic_function(SrcLocation loc, const Function std::vector parameters; parameters.reserve(fun_ref->get_num_params()); for (const LocalVarData& orig_p : fun_ref->parameters) { - parameters.emplace_back(orig_p.name, orig_p.loc, replace_genericT_with_deduced(orig_p.declared_type, fun_ref->genericTs, substitutionTs), orig_p.flags, orig_p.idx); + parameters.emplace_back(orig_p.name, orig_p.loc, replace_genericT_with_deduced(orig_p.declared_type, fun_ref->genericTs, substitutionTs), orig_p.flags, orig_p.param_idx); } TypePtr declared_return_type = replace_genericT_with_deduced(fun_ref->declared_return_type, fun_ref->genericTs, substitutionTs); const GenericsInstantiation* instantiationTs = new GenericsInstantiation(loc, std::move(substitutionTs)); diff --git a/tolk/pipe-ast-to-legacy.cpp b/tolk/pipe-ast-to-legacy.cpp index d60bb8b34..c64fe751e 100644 --- a/tolk/pipe-ast-to-legacy.cpp +++ b/tolk/pipe-ast-to-legacy.cpp @@ -34,15 +34,15 @@ namespace tolk { struct LValGlobs { - std::vector> globs; + std::vector>> globs; - void add_modified_glob(const GlobalVarData* g_sym, var_idx_t local_ir_idx) { - globs.emplace_back(g_sym, local_ir_idx); + void add_modified_glob(const GlobalVarData* g_sym, std::vector local_ir_idx) { + globs.emplace_back(g_sym, std::move(local_ir_idx)); } void gen_ops_set_globs(CodeBlob& code, SrcLocation loc) const { for (const auto& [g_sym, ir_idx] : globs) { - Op& op = code.emplace_back(loc, Op::_SetGlob, std::vector{}, std::vector{ ir_idx }, g_sym); + Op& op = code.emplace_back(loc, Op::_SetGlob, std::vector{}, ir_idx, g_sym); op.set_impure_flag(); } } @@ -93,7 +93,9 @@ static std::vector> pre_compile_tensor_inner(CodeBlob& co void on_var_modified(var_idx_t ir_idx, SrcLocation loc, CodeBlob& code) { tolk_assert(is_watched(ir_idx)); - var_idx_t tmp_idx = code.create_tmp_var(code.vars[ir_idx].v_type, loc); + std::vector tmp_idx_arr = code.create_tmp_var(code.vars[ir_idx].v_type, loc); + tolk_assert(tmp_idx_arr.size() == 1); + var_idx_t tmp_idx = tmp_idx_arr[0]; code.emplace_back(loc, Op::_Let, std::vector{tmp_idx}, std::vector{ir_idx}); for (std::vector& prev_vars : res_lists) { std::replace(prev_vars.begin(), prev_vars.end(), ir_idx, tmp_idx); @@ -143,7 +145,7 @@ static std::vector pre_compile_let(CodeBlob& code, AnyExprV lhs, AnyE std::vector right = pre_compile_expr(rhs, code); const TypeDataTypedTuple* inferred_tuple = rhs->inferred_type->try_as(); std::vector types_list = inferred_tuple->items; - std::vector rvect = {code.create_tmp_var(TypeDataTensor::create(std::move(types_list)), rhs->loc)}; + std::vector rvect = code.create_tmp_var(TypeDataTensor::create(std::move(types_list)), rhs->loc); code.emplace_back(lhs->loc, Op::_UnTuple, rvect, std::move(right)); LValGlobs globs; std::vector left = pre_compile_tensor(code, lhs->as()->get_items(), &globs); @@ -164,7 +166,7 @@ static std::vector pre_compile_let(CodeBlob& code, AnyExprV lhs, AnyE static std::vector gen_op_call(CodeBlob& code, TypePtr ret_type, SrcLocation here, std::vector&& args_vars, const FunctionData* fun_ref) { - std::vector rvect = {code.create_tmp_var(ret_type, here)}; + std::vector rvect = code.create_tmp_var(ret_type, here); Op& op = code.emplace_back(here, Op::_Call, rvect, std::move(args_vars), fun_ref); if (!fun_ref->is_marked_as_pure()) { op.set_impure_flag(); @@ -175,9 +177,9 @@ static std::vector gen_op_call(CodeBlob& code, TypePtr ret_type, SrcL static std::vector process_symbol(SrcLocation loc, const Symbol* sym, CodeBlob& code, LValGlobs* lval_globs) { if (const auto* glob_ref = sym->try_as()) { - std::vector rvect = {code.create_tmp_var(glob_ref->declared_type, loc)}; + std::vector rvect = code.create_tmp_var(glob_ref->declared_type, loc); if (lval_globs) { - lval_globs->add_modified_glob(glob_ref, rvect[0]); + lval_globs->add_modified_glob(glob_ref, rvect); return rvect; } else { code.emplace_back(loc, Op::_GlobVar, rvect, std::vector{}, glob_ref); @@ -186,22 +188,25 @@ static std::vector process_symbol(SrcLocation loc, const Symbol* sym, } if (const auto* const_ref = sym->try_as()) { if (const_ref->is_int_const()) { - std::vector rvect = {code.create_tmp_var(TypeDataInt::create(), loc)}; + std::vector rvect = code.create_tmp_var(TypeDataInt::create(), loc); code.emplace_back(loc, Op::_IntConst, rvect, const_ref->as_int_const()); return rvect; } else { - std::vector rvect = {code.create_tmp_var(TypeDataSlice::create(), loc)}; + std::vector rvect = code.create_tmp_var(TypeDataSlice::create(), loc); code.emplace_back(loc, Op::_SliceConst, rvect, const_ref->as_slice_const()); return rvect; } } if (const auto* fun_ref = sym->try_as()) { - std::vector rvect = {code.create_tmp_var(fun_ref->inferred_full_type, loc)}; + std::vector rvect = code.create_tmp_var(fun_ref->inferred_full_type, loc); code.emplace_back(loc, Op::_GlobVar, rvect, std::vector{}, fun_ref); return rvect; } if (const auto* var_ref = sym->try_as()) { - return {var_ref->idx}; +#ifdef TOLK_DEBUG + tolk_assert(static_cast(var_ref->ir_idx.size()) == var_ref->declared_type->calc_width_on_stack()); +#endif + return var_ref->ir_idx; } throw Fatal("process_symbol"); } @@ -244,7 +249,7 @@ static std::vector process_binary_operator(V v, v_b_ne_0->mutate()->assign_fun_ref(lookup_global_symbol("_!=_")->as()); std::vector cond = pre_compile_expr(v->get_lhs(), code); tolk_assert(cond.size() == 1); - std::vector rvect = {code.create_tmp_var(v->inferred_type, v->loc)}; + std::vector rvect = code.create_tmp_var(v->inferred_type, v->loc); Op& if_op = code.emplace_back(v->loc, Op::_If, cond); code.push_set_cur(if_op.block0); code.emplace_back(v->loc, Op::_Let, rvect, pre_compile_expr(t == tok_logical_and ? v_b_ne_0 : v_1, code)); @@ -266,7 +271,7 @@ static std::vector process_unary_operator(V v, Co static std::vector process_ternary_operator(V v, CodeBlob& code) { std::vector cond = pre_compile_expr(v->get_cond(), code); tolk_assert(cond.size() == 1); - std::vector rvect = {code.create_tmp_var(v->inferred_type, v->loc)}; + std::vector rvect = code.create_tmp_var(v->inferred_type, v->loc); Op& if_op = code.emplace_back(v->loc, Op::_If, cond); code.push_set_cur(if_op.block0); code.emplace_back(v->get_when_true()->loc, Op::_Let, rvect, pre_compile_expr(v->get_when_true(), code)); @@ -299,7 +304,7 @@ static std::vector process_function_call(V v, Code std::vector tfunc = pre_compile_expr(v->get_callee(), code); tolk_assert(tfunc.size() == 1); args_vars.push_back(tfunc[0]); - std::vector rvect = {code.create_tmp_var(v->inferred_type, v->loc)}; + std::vector rvect = code.create_tmp_var(v->inferred_type, v->loc); Op& op = code.emplace_back(v->loc, Op::_CallInd, rvect, std::move(args_vars)); op.set_impure_flag(); return rvect; @@ -361,8 +366,8 @@ static std::vector process_function_call(V v, Code } } } - std::vector rvect = {code.create_tmp_var(real_ret_type, v->loc)}; - left.push_back(rvect[0]); + std::vector rvect = code.create_tmp_var(real_ret_type, v->loc); + left.insert(left.end(), rvect.begin(), rvect.end()); code.on_var_modification(left, v->loc); code.emplace_back(v->loc, Op::_Let, std::move(left), rvect_apply); local_globs.gen_ops_set_globs(code, v->loc); @@ -388,21 +393,21 @@ static std::vector process_typed_tuple(V v, CodeBlob if (lval_globs) { // todo some time, make "var (a, [b,c]) = (1, [2,3])" work v->error("[...] can not be used as lvalue here"); } - std::vector left = std::vector{code.create_tmp_var(v->inferred_type, v->loc)}; + std::vector left = code.create_tmp_var(v->inferred_type, v->loc); std::vector right = pre_compile_tensor(code, v->get_items()); code.emplace_back(v->loc, Op::_Tuple, left, std::move(right)); return left; } static std::vector process_int_const(V v, CodeBlob& code) { - std::vector rvect = {code.create_tmp_var(v->inferred_type, v->loc)}; + std::vector rvect = code.create_tmp_var(v->inferred_type, v->loc); code.emplace_back(v->loc, Op::_IntConst, rvect, v->intval); return rvect; } static std::vector process_string_const(V v, CodeBlob& code) { ConstantValue value = eval_const_init_value(v); - std::vector rvect = {code.create_tmp_var(v->inferred_type, v->loc)}; + std::vector rvect = code.create_tmp_var(v->inferred_type, v->loc); if (value.is_int()) { code.emplace_back(v->loc, Op::_IntConst, rvect, value.as_int()); } else { @@ -426,9 +431,9 @@ static std::vector process_local_var(V v, CodeBlob return process_symbol(v->loc, v->var_ref, code, nullptr); } - tolk_assert(v->var_ref->idx == -1); - v->var_ref->mutate()->assign_idx(code.create_var(v->inferred_type, v->var_ref, v->loc)); - return {v->var_ref->idx}; + tolk_assert(v->var_ref->ir_idx.empty()); + v->var_ref->mutate()->assign_ir_idx(code.create_var(v->inferred_type, v->var_ref, v->loc)); + return v->var_ref->ir_idx; } static std::vector process_local_vars_declaration(V, CodeBlob&) { @@ -439,7 +444,7 @@ static std::vector process_local_vars_declaration(V process_underscore(V v, CodeBlob& code) { // when _ is used as left side of assignment, like `(cs, _) = cs.loadAndReturn()` - return {code.create_tmp_var(v->inferred_type, v->loc)}; + return code.create_tmp_var(v->inferred_type, v->loc); } std::vector pre_compile_expr(AnyExprV v, CodeBlob& code, LValGlobs* lval_globs) { @@ -516,8 +521,8 @@ static void process_assert_statement(V v, CodeBlob& code) static void process_catch_variable(AnyExprV v_catch_var, CodeBlob& code) { if (auto v_ref = v_catch_var->try_as(); v_ref && v_ref->sym) { // not underscore const LocalVarData* var_ref = v_ref->sym->as(); - tolk_assert(var_ref->idx == -1); - var_ref->mutate()->assign_idx(code.create_var(v_catch_var->inferred_type, var_ref, v_catch_var->loc)); + tolk_assert(var_ref->ir_idx.empty()); + var_ref->mutate()->assign_ir_idx(code.create_var(v_catch_var->inferred_type, var_ref, v_catch_var->loc)); } } @@ -627,14 +632,13 @@ static void process_throw_statement(V v, CodeBlob& code) { static void process_return_statement(V v, CodeBlob& code) { std::vector return_vars = v->has_return_value() ? pre_compile_expr(v->get_return_value(), code) : std::vector{}; if (code.fun_ref->does_return_self()) { - tolk_assert(return_vars.size() == 1); return_vars = {}; } if (code.fun_ref->has_mutate_params()) { std::vector mutated_vars; for (const LocalVarData& p_sym: code.fun_ref->parameters) { if (p_sym.is_mutate_parameter()) { - mutated_vars.push_back(p_sym.idx); + mutated_vars.insert(mutated_vars.end(), p_sym.ir_idx.begin(), p_sym.ir_idx.end()); } } return_vars.insert(return_vars.begin(), mutated_vars.begin(), mutated_vars.end()); @@ -647,7 +651,7 @@ static void append_implicit_return_statement(SrcLocation loc_end, CodeBlob& code if (code.fun_ref->has_mutate_params()) { for (const LocalVarData& p_sym: code.fun_ref->parameters) { if (p_sym.is_mutate_parameter()) { - mutated_vars.push_back(p_sym.idx); + mutated_vars.insert(mutated_vars.end(), p_sym.ir_idx.begin(), p_sym.ir_idx.end()); } } } @@ -685,11 +689,23 @@ void process_any_statement(AnyV v, CodeBlob& code) { static void convert_function_body_to_CodeBlob(const FunctionData* fun_ref, FunctionBodyCode* code_body) { auto v_body = fun_ref->ast_root->as()->get_body()->as(); CodeBlob* blob = new CodeBlob{fun_ref->name, fun_ref->loc, fun_ref}; - FormalArgList legacy_arg_list; - for (const LocalVarData& param : fun_ref->parameters) { - legacy_arg_list.emplace_back(param.declared_type, ¶m, param.loc); + + std::vector rvect_import; + int total_arg_width = 0; + for (int i = 0; i < fun_ref->get_num_params(); ++i) { + total_arg_width += fun_ref->parameters[i].declared_type->calc_width_on_stack(); + } + rvect_import.reserve(total_arg_width); + + for (int i = 0; i < fun_ref->get_num_params(); ++i) { + const LocalVarData& param_i = fun_ref->parameters[i]; + std::vector ir_idx = blob->create_var(param_i.declared_type, ¶m_i, param_i.loc); + rvect_import.insert(rvect_import.end(), ir_idx.begin(), ir_idx.end()); + param_i.mutate()->assign_ir_idx(std::move(ir_idx)); } - blob->import_params(std::move(legacy_arg_list)); + blob->emplace_back(fun_ref->loc, Op::_Import, rvect_import); + blob->in_var_cnt = blob->var_cnt; + tolk_assert(blob->var_cnt == total_arg_width); for (AnyV item : v_body->get_items()) { process_any_statement(item, *blob); diff --git a/tolk/pipe-check-rvalue-lvalue.cpp b/tolk/pipe-check-rvalue-lvalue.cpp index 038b09991..943dfb96c 100644 --- a/tolk/pipe-check-rvalue-lvalue.cpp +++ b/tolk/pipe-check-rvalue-lvalue.cpp @@ -38,7 +38,7 @@ static void fire_error_cannot_be_used_as_lvalue(AnyV v, const std::string& detai GNU_ATTRIBUTE_NORETURN GNU_ATTRIBUTE_COLD static void fire_error_modifying_immutable_variable(AnyExprV v, const LocalVarData* var_ref) { - if (var_ref->idx == 0 && var_ref->name == "self") { + if (var_ref->param_idx == 0 && var_ref->name == "self") { v->error("modifying `self`, which is immutable by default; probably, you want to declare `mutate self`"); } else { v->error("modifying immutable variable `" + var_ref->name + "`"); diff --git a/tolk/pipe-generate-fif-output.cpp b/tolk/pipe-generate-fif-output.cpp index 9092e5647..7ef6ba7bc 100644 --- a/tolk/pipe-generate-fif-output.cpp +++ b/tolk/pipe-generate-fif-output.cpp @@ -54,11 +54,6 @@ static void generate_output_func(const FunctionData* fun_ref) { std::cerr << "after prune_unreachable: \n"; code->print(std::cerr, 0); } - code->split_vars(true); - if (G.is_verbosity(5)) { - std::cerr << "after split_vars: \n"; - code->print(std::cerr, 0); - } for (int i = 0; i < 8; i++) { code->compute_used_code_vars(); if (G.is_verbosity(4)) { diff --git a/tolk/pipe-refine-lvalue-for-mutate.cpp b/tolk/pipe-refine-lvalue-for-mutate.cpp index 45dd3a94f..540d74137 100644 --- a/tolk/pipe-refine-lvalue-for-mutate.cpp +++ b/tolk/pipe-refine-lvalue-for-mutate.cpp @@ -38,11 +38,11 @@ static void fire_error_invalid_mutate_arg_passed(AnyExprV v, const FunctionData* std::string arg_str(arg_expr->type == ast_reference ? arg_expr->as()->get_name() : "obj"); // case: `loadInt(cs, 32)`; suggest: `cs.loadInt(32)` - if (p_sym.is_mutate_parameter() && !arg_passed_as_mutate && !called_as_method && p_sym.idx == 0 && fun_ref->does_accept_self()) { + if (p_sym.is_mutate_parameter() && !arg_passed_as_mutate && !called_as_method && p_sym.param_idx == 0 && fun_ref->does_accept_self()) { v->error("`" + fun_ref->name + "` is a mutating method; consider calling `" + arg_str + "." + fun_ref->name + "()`, not `" + fun_ref->name + "(" + arg_str + ")`"); } // case: `cs.mutating_function()`; suggest: `mutating_function(mutate cs)` or make it a method - if (p_sym.is_mutate_parameter() && called_as_method && p_sym.idx == 0 && !fun_ref->does_accept_self()) { + if (p_sym.is_mutate_parameter() && called_as_method && p_sym.param_idx == 0 && !fun_ref->does_accept_self()) { v->error("function `" + fun_ref->name + "` mutates parameter `" + p_sym.name + "`; consider calling `" + fun_ref->name + "(mutate " + arg_str + ")`, not `" + arg_str + "." + fun_ref->name + "`(); alternatively, rename parameter to `self` to make it a method"); } // case: `mutating_function(arg)`; suggest: `mutate arg` diff --git a/tolk/symtable.cpp b/tolk/symtable.cpp index 918fdab33..c56dc6ed8 100644 --- a/tolk/symtable.cpp +++ b/tolk/symtable.cpp @@ -93,8 +93,8 @@ void GlobalConstData::assign_resolved_type(TypePtr declared_type) { this->declared_type = declared_type; } -void LocalVarData::assign_idx(int idx) { - this->idx = idx; +void LocalVarData::assign_ir_idx(std::vector&& ir_idx) { + this->ir_idx = std::move(ir_idx); } void LocalVarData::assign_resolved_type(TypePtr declared_type) { diff --git a/tolk/symtable.h b/tolk/symtable.h index 3cda24edf..27753cebd 100644 --- a/tolk/symtable.h +++ b/tolk/symtable.h @@ -59,20 +59,23 @@ struct LocalVarData final : Symbol { TypePtr declared_type; // either at declaration `var x:int`, or if omitted, from assigned value `var x=2` int flags; - int idx; + int param_idx; // 0...N for function parameters, -1 for local vars + std::vector ir_idx; - LocalVarData(std::string name, SrcLocation loc, TypePtr declared_type, int flags, int idx) + LocalVarData(std::string name, SrcLocation loc, TypePtr declared_type, int flags, int param_idx) : Symbol(std::move(name), loc) , declared_type(declared_type) , flags(flags) - , idx(idx) { + , param_idx(param_idx) { } + bool is_parameter() const { return param_idx >= 0; } + bool is_immutable() const { return flags & flagImmutable; } bool is_mutate_parameter() const { return flags & flagMutateParameter; } LocalVarData* mutate() const { return const_cast(this); } - void assign_idx(int idx); + void assign_ir_idx(std::vector&& ir_idx); void assign_resolved_type(TypePtr declared_type); void assign_inferred_type(TypePtr inferred_type); }; diff --git a/tolk/tolk.h b/tolk/tolk.h index 5ec4d3e08..7b44931ee 100644 --- a/tolk/tolk.h +++ b/tolk/tolk.h @@ -45,17 +45,15 @@ typedef int const_idx_t; struct TmpVar { TypePtr v_type; - var_idx_t idx; + var_idx_t ir_idx; const LocalVarData* v_sym; // points to var defined in code; nullptr for implicitly created tmp vars - int coord; SrcLocation where; std::vector> on_modification; - TmpVar(var_idx_t _idx, TypePtr type, const LocalVarData* v_sym, SrcLocation loc) + TmpVar(var_idx_t ir_idx, TypePtr type, const LocalVarData* v_sym, SrcLocation loc) : v_type(type) - , idx(_idx) + , ir_idx(ir_idx) , v_sym(v_sym) - , coord(0) , where(loc) { } @@ -345,8 +343,6 @@ struct Op { void show_var_list(std::ostream& os, const std::vector& list, const std::vector& vars) const; static void show_block(std::ostream& os, const Op* block, const std::vector& vars, std::string pfx = "", int mode = 0); - 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 disabled = false); bool set_var_info(const VarDescrList& new_var_info); @@ -385,9 +381,6 @@ inline ListIterator end(const Op* op_list) { return ListIterator{}; } -typedef std::tuple FormalArg; -typedef std::vector FormalArgList; - struct AsmOpList; struct FunctionBodyCode { @@ -1115,12 +1108,10 @@ struct CodeBlob { #endif return res; } - bool import_params(FormalArgList&& arg_list); - var_idx_t create_var(TypePtr var_type, const LocalVarData* v_sym, SrcLocation loc); - var_idx_t create_tmp_var(TypePtr var_type, SrcLocation loc) { + std::vector create_var(TypePtr var_type, const LocalVarData* v_sym, SrcLocation loc); + std::vector create_tmp_var(TypePtr var_type, SrcLocation loc) { return create_var(var_type, nullptr, loc); } - int split_vars(bool strict = false); bool compute_used_code_vars(); bool compute_used_code_vars(std::unique_ptr& ops, const VarDescrList& var_info, bool edit) const; void print(std::ostream& os, int flags = 0) const; diff --git a/tolk/type-system.cpp b/tolk/type-system.cpp index 72419d8c1..401c72af5 100644 --- a/tolk/type-system.cpp +++ b/tolk/type-system.cpp @@ -537,31 +537,6 @@ bool TypeDataVoid::can_be_casted_with_as_operator(TypePtr cast_to) const { } -// -------------------------------------------- -// extract_components() -// -// used in code generation (transforming Ops to other Ops) -// to be removed in the future -// - -void TypeDataGenericT::extract_components(std::vector& comp_types) const { - assert(false); -} - -void TypeDataTensor::extract_components(std::vector& comp_types) const { - for (TypePtr item : items) { - item->extract_components(comp_types); - } -} - -void TypeDataUnresolved::extract_components(std::vector& comp_types) const { - assert(false); -} - -void TypeDataVoid::extract_components(std::vector& comp_types) const { -} - - // -------------------------------------------- // parsing type from tokens // diff --git a/tolk/type-system.h b/tolk/type-system.h index 0db427091..482039e61 100644 --- a/tolk/type-system.h +++ b/tolk/type-system.h @@ -97,10 +97,6 @@ class TypeData { virtual int calc_width_on_stack() const { return 1; } - - virtual void extract_components(std::vector& comp_types) const { - comp_types.push_back(this); - } }; /* @@ -291,7 +287,6 @@ class TypeDataGenericT final : public TypeData { bool can_rhs_be_assigned(TypePtr rhs) const override; bool can_be_casted_with_as_operator(TypePtr cast_to) const override; int calc_width_on_stack() const override; - void extract_components(std::vector& comp_types) const override; }; /* @@ -318,7 +313,6 @@ class TypeDataTensor final : public TypeData { void traverse(const TraverserCallbackT& callback) const override; TypePtr replace_children_custom(const ReplacerCallbackT& callback) const override; int calc_width_on_stack() const override; - void extract_components(std::vector& comp_types) const override; }; /* @@ -387,7 +381,6 @@ class TypeDataUnresolved final : public TypeData { bool can_rhs_be_assigned(TypePtr rhs) const override; bool can_be_casted_with_as_operator(TypePtr cast_to) const override; int calc_width_on_stack() const override; - void extract_components(std::vector& comp_types) const override; }; /* @@ -408,7 +401,6 @@ class TypeDataVoid final : public TypeData { bool can_rhs_be_assigned(TypePtr rhs) const override; bool can_be_casted_with_as_operator(TypePtr cast_to) const override; int calc_width_on_stack() const override; - void extract_components(std::vector& comp_types) const override; }; From 7a1602f591cc7db4ea9ce12a84dc3c4bf90dd32e Mon Sep 17 00:00:00 2001 From: tolk-vm Date: Mon, 27 Jan 2025 10:29:17 +0300 Subject: [PATCH 33/38] [Tolk] Support syntax `tensorVar.0` and `tupleVar.0` It works both for reading and writing: > var t = (1, 2); > t.0; // 1 > t.0 = 5; > t; // (5, 2) It also works for typed/untyped tuples, producing INDEX and SETINDEX. Global tensors and tuples works. Nesting `t.0.1.2` works. `mutate` works. Even mixing tuples inside tensors inside a global for writing works. --- crypto/smartcont/tolk-stdlib/common.tolk | 17 +- tolk-tester/tests/a10.tolk | 32 +- tolk-tester/tests/a6_1.tolk | 4 +- .../tests/allow_post_modification.tolk | 13 +- tolk-tester/tests/bit-operators.tolk | 54 +-- tolk-tester/tests/cells-slices.tolk | 20 +- tolk-tester/tests/codegen_check_demo.tolk | 6 +- tolk-tester/tests/generics-1.tolk | 19 +- tolk-tester/tests/if_stmt.tolk | 6 +- tolk-tester/tests/indexed-access.tolk | 287 ++++++++++++ tolk-tester/tests/invalid-assign-1.tolk | 9 + tolk-tester/tests/invalid-assign-2.tolk | 11 + tolk-tester/tests/invalid-assign-3.tolk | 10 + tolk-tester/tests/invalid-assign-4.tolk | 10 + tolk-tester/tests/invalid-assign-5.tolk | 9 + tolk-tester/tests/invalid-assign-6.tolk | 9 + tolk-tester/tests/invalid-assign-7.tolk | 8 + tolk-tester/tests/invalid-call-1.tolk | 2 +- tolk-tester/tests/invalid-call-10.tolk | 11 + tolk-tester/tests/invalid-call-11.tolk | 11 + tolk-tester/tests/invalid-call-7.tolk | 2 +- tolk-tester/tests/invalid-call-8.tolk | 4 +- tolk-tester/tests/invalid-generics-12.tolk | 15 + tolk-tester/tests/invalid-typing-13.tolk | 9 + tolk-tester/tests/known-bugs.tolk | 27 -- tolk-tester/tests/logical-operators.tolk | 40 +- tolk-tester/tests/mutate-methods.tolk | 12 +- tolk-tester/tests/no-spaces.tolk | 8 +- tolk-tester/tests/null-keyword.tolk | 38 +- tolk-tester/tests/op-priority.tolk | 24 +- tolk-tester/tests/use-before-declare.tolk | 2 +- tolk-tester/tests/var-apply.tolk | 10 + tolk/abscode.cpp | 63 +-- tolk/asmops.cpp | 19 +- tolk/ast-from-tokens.cpp | 5 +- tolk/ast.h | 10 +- tolk/builtins.cpp | 14 + tolk/codegen.cpp | 2 +- tolk/pipe-ast-to-legacy.cpp | 437 ++++++++++++++---- tolk/pipe-check-rvalue-lvalue.cpp | 4 +- tolk/pipe-infer-types-and-calls.cpp | 110 ++++- tolk/tolk.h | 54 +-- 42 files changed, 1119 insertions(+), 338 deletions(-) create mode 100644 tolk-tester/tests/indexed-access.tolk create mode 100644 tolk-tester/tests/invalid-assign-1.tolk create mode 100644 tolk-tester/tests/invalid-assign-2.tolk create mode 100644 tolk-tester/tests/invalid-assign-3.tolk create mode 100644 tolk-tester/tests/invalid-assign-4.tolk create mode 100644 tolk-tester/tests/invalid-assign-5.tolk create mode 100644 tolk-tester/tests/invalid-assign-6.tolk create mode 100644 tolk-tester/tests/invalid-assign-7.tolk create mode 100644 tolk-tester/tests/invalid-call-10.tolk create mode 100644 tolk-tester/tests/invalid-call-11.tolk create mode 100644 tolk-tester/tests/invalid-generics-12.tolk create mode 100644 tolk-tester/tests/invalid-typing-13.tolk delete mode 100644 tolk-tester/tests/known-bugs.tolk diff --git a/crypto/smartcont/tolk-stdlib/common.tolk b/crypto/smartcont/tolk-stdlib/common.tolk index 46068a206..4c0c40075 100644 --- a/crypto/smartcont/tolk-stdlib/common.tolk +++ b/crypto/smartcont/tolk-stdlib/common.tolk @@ -21,23 +21,32 @@ fun tuplePush(mutate self: tuple, value: T): void asm "TPUSH"; /// Returns the first element of a non-empty tuple. +/// `t.0` is actually the same as `t.tupleFirst()` @pure -fun tupleFirst(t: tuple): T +fun tupleFirst(self: tuple): T asm "FIRST"; /// Returns the [`index`]-th element of a tuple. +/// `t.i` is actually the same as `t.tupleAt(i)` @pure -fun tupleAt(t: tuple, index: int): T +fun tupleAt(self: tuple, index: int): T + builtin; + +/// Sets the [`index`]-th element of a tuple to a specified value +/// (element with this index must already exist, a new element isn't created). +/// `t.i = value` is actually the same as `t.tupleSetAt(value, i)` +@pure +fun tupleSetAt(mutate self: tuple, value: T, index: int): void builtin; /// Returns the size of a tuple (elements count in it). @pure -fun tupleSize(t: tuple): int +fun tupleSize(self: tuple): int asm "TLEN"; /// Returns the last element of a non-empty tuple. @pure -fun tupleLast(t: tuple): T +fun tupleLast(self: tuple): T asm "LAST"; diff --git a/tolk-tester/tests/a10.tolk b/tolk-tester/tests/a10.tolk index af1042487..755a3bfbc 100644 --- a/tolk-tester/tests/a10.tolk +++ b/tolk-tester/tests/a10.tolk @@ -78,6 +78,17 @@ fun testStartBalanceCodegen2() { return first; } +global cur: [int, int, int]; +global next: [int, int, int]; + +@method_id(95) +fun test95() { + cur = [1, 2, 3]; + next = [2, 3, 4]; + (cur, next) = (next, [3, 4, 5]); + return (cur, next); +} + /** method_id | in | out @testcase | 0 | 101 15 | 100 1 @@ -90,6 +101,7 @@ fun testStartBalanceCodegen2() { @testcase | 89 | 4 | 1 4 1 4 @testcase | 91 | | 10 @testcase | 92 | | 10 32 +@testcase | 95 | | [ 2 3 4 ] [ 3 4 5 ] @fif_codegen """ @@ -104,9 +116,9 @@ fun testStartBalanceCodegen2() { testDumpDontPolluteStack PROC:<{ ... DUMPSTK - x{6d79} PUSHSLICE // f s _5 + x{6d79} PUSHSLICE // f s '5 STRDUMP DROP - SBITS // f _6 + SBITS // f '6 }> """ @@ -127,4 +139,20 @@ fun testStartBalanceCodegen2() { FIRST // first }> """ + +@fif_codegen +""" + test95 PROC:<{ + ... + next GETGLOB // '10 + 3 PUSHINT // '10 '12=3 + 4 PUSHINT // '10 '12=3 '13=4 + 5 PUSHINT // '10 '12=3 '13=4 '14=5 + TRIPLE // '15 '16 + next SETGLOB + cur SETGLOB + cur GETGLOB // '17 + next GETGLOB // '17 '18 + }> +""" */ diff --git a/tolk-tester/tests/a6_1.tolk b/tolk-tester/tests/a6_1.tolk index 4995c42d3..8079972b1 100644 --- a/tolk-tester/tests/a6_1.tolk +++ b/tolk-tester/tests/a6_1.tolk @@ -7,7 +7,7 @@ fun main(a: int, b: int, c: int, d: int, e: int, f: int): (int, int) { @method_id(101) fun testDivMod(x: int, y: int) { - return [divMod(x, y), modDiv(x, y), mulDivMod(x, y, 10)]; + return (divMod(x, y), modDiv(x, y), mulDivMod(x, y, 10)); } /** @@ -18,5 +18,5 @@ fun testDivMod(x: int, y: int) { @testcase | 0 | 448 -433 -444 792 150012 -356232 | -218 -572 @testcase | 0 | -40 -821 433 -734 -721629 -741724 | -206 889 @testcase | 0 | -261 -98 -494 868 -166153 733738 | 263 995 -@testcase | 101 | 112 3 | [ 37 1 1 37 33 6 ] +@testcase | 101 | 112 3 | 37 1 1 37 33 6 */ diff --git a/tolk-tester/tests/allow_post_modification.tolk b/tolk-tester/tests/allow_post_modification.tolk index e374f62b3..df758a1ec 100644 --- a/tolk-tester/tests/allow_post_modification.tolk +++ b/tolk-tester/tests/allow_post_modification.tolk @@ -89,12 +89,14 @@ fun test_if_else(x: int): (int, int, int, int, int) { @method_id(21) fun test_assign_with_inner(x: int) { - return (x, x += 10, [(x, x += 20, eq(x -= 50), x)], eq2((x, x *= eq(x /= 2)))); + var result = (x, x += 10, [x, x += 20, eq(x -= 50), x], eq2((x, x *= eq(x /= 2)))); + return result; } @method_id(22) fun test_assign_with_mutate(x: int) { - return (x, mul2(mutate x, x += 5), x.`~inc`(mul2(mutate x, x)), x); + var (result, _) = ((x, mul2(mutate x, x += 5), x.`~inc`(mul2(mutate x, x)), x), 0); + return result; } @method_id(23) @@ -138,5 +140,12 @@ fun main() { inc CALLDICT // self newY }> """ + +@fif_codegen +""" + test_assign_tensor_global PROC:<{ + // x.0 x.1 +""" + @code_hash 7627024945492125068389905298530400936797031708759561372406088054030801992712 */ diff --git a/tolk-tester/tests/bit-operators.tolk b/tolk-tester/tests/bit-operators.tolk index 4cb8e1ba1..b01068839 100644 --- a/tolk-tester/tests/bit-operators.tolk +++ b/tolk-tester/tests/bit-operators.tolk @@ -127,10 +127,10 @@ fun testBoolCompareOptimized(x: bool) { """ boolWithBitwiseConst PROC:<{ // - 0 PUSHINT // _3 - -1 PUSHINT // _3 _5 - 0 PUSHINT // _3 _5 _7 - -1 PUSHINT // _3 _5 _7 _8 + 0 PUSHINT // '3 + -1 PUSHINT // '3 '5 + 0 PUSHINT // '3 '5 '7 + -1 PUSHINT // '3 '5 '7 '8 }> """ @@ -142,22 +142,22 @@ fun testBoolCompareOptimized(x: bool) { UNTIL:<{ INC // i n cnt s2 PUSH // i n cnt i - NOT // i n cnt _6 + NOT // i n cnt '6 }> // i n cnt UNTIL:<{ INC // i n cnt s2 PUSH // i n cnt i - NOT // i n cnt _9 + NOT // i n cnt '9 }> // i n cnt UNTIL:<{ INC // i n cnt OVER // i n cnt n - 0 EQINT // i n cnt _12 + 0 EQINT // i n cnt '12 }> // i n cnt s0 s2 XCHG // cnt n i - NOT // cnt n _13 - SWAP // cnt _13 n - 0 EQINT // cnt _13 _14 + NOT // cnt n '13 + SWAP // cnt '13 n + 0 EQINT // cnt '13 '14 }> """ @@ -165,12 +165,12 @@ fun testBoolCompareOptimized(x: bool) { """ testConstNegateCodegen PROC:<{ // - TRUE // _0 - FALSE // _0 _1 - FALSE // _0 _1 _2 - TRUE // _0 _1 _2 _3 - TRUE // _0 _1 _2 _3 _4 - FALSE // _0 _1 _2 _3 _4 _5 + TRUE // '0 + FALSE // '0 '1 + FALSE // '0 '1 '2 + TRUE // '0 '1 '2 '3 + TRUE // '0 '1 '2 '3 '4 + FALSE // '0 '1 '2 '3 '4 '5 }> """ @@ -179,11 +179,11 @@ fun testBoolCompareOptimized(x: bool) { testBoolNegateOptimized PROC:<{ // x DUP // x x - NOT // x _1 - OVER // x _1 x - NOT // x _1 _2 + NOT // x '1 + OVER // x '1 x + NOT // x '1 '2 s2 s(-1) PUXC - TRUE // x _1 x _2 _3 + TRUE // x '1 x '2 '3 }> """ @@ -192,13 +192,13 @@ fun testBoolCompareOptimized(x: bool) { testBoolCompareOptimized PROC:<{ // x DUP // x x - NOT // x _1 - OVER // x _1 x - eqX CALLDICT // x _1 _2 - NOT // x _1 _3 - s2 PUSH // x _1 _3 x - eqX CALLDICT // x _1 _3 _4 - s3 PUSH // x _1 _3 _4 x + NOT // x '1 + OVER // x '1 x + eqX CALLDICT // x '1 '2 + NOT // x '1 '3 + s2 PUSH // x '1 '3 x + eqX CALLDICT // x '1 '3 '4 + s3 PUSH // x '1 '3 '4 x }> """ */ diff --git a/tolk-tester/tests/cells-slices.tolk b/tolk-tester/tests/cells-slices.tolk index 0a2807e6b..19e2e2153 100644 --- a/tolk-tester/tests/cells-slices.tolk +++ b/tolk-tester/tests/cells-slices.tolk @@ -216,16 +216,16 @@ Note, that since 'compute-asm-ltr' became on be default, chaining methods codege """ test6 PROC:<{ // - NEWC // _0 - 1 PUSHINT // _0 _1=1 - SWAP // _1=1 _0 - 32 STU // _0 - 2 PUSHINT // _0 _4=2 - SWAP // _4=2 _0 - 32 STU // _0 - 3 PUSHINT // _0 _7=3 - SWAP // _7=3 _0 - 32 STU // _0 + NEWC // '0 + 1 PUSHINT // '0 '1=1 + SWAP // '1=1 '0 + 32 STU // '0 + 2 PUSHINT // '0 '4=2 + SWAP // '4=2 '0 + 32 STU // '0 + 3 PUSHINT // '0 '7=3 + SWAP // '7=3 '0 + 32 STU // '0 }> """ */ diff --git a/tolk-tester/tests/codegen_check_demo.tolk b/tolk-tester/tests/codegen_check_demo.tolk index e40f03779..b355a9b77 100644 --- a/tolk-tester/tests/codegen_check_demo.tolk +++ b/tolk-tester/tests/codegen_check_demo.tolk @@ -35,7 +35,7 @@ Below, I just give examples of @fif_codegen tag: """ main PROC:<{ // s - 17 PUSHINT // s _1=17 + 17 PUSHINT // s '1=17 OVER // s z=17 t WHILE:<{ ... @@ -63,7 +63,7 @@ main PROC:<{ @fif_codegen """ OVER - 0 GTINT // s z t _5 + 0 GTINT // s z t '5 """ @fif_codegen @@ -83,7 +83,7 @@ FALSE }> """ -@fif_codegen NOT // _8 +@fif_codegen NOT // '8 @fif_codegen main PROC:<{ @fif_codegen_avoid PROCINLINE diff --git a/tolk-tester/tests/generics-1.tolk b/tolk-tester/tests/generics-1.tolk index 5a649a440..453ec2823 100644 --- a/tolk-tester/tests/generics-1.tolk +++ b/tolk-tester/tests/generics-1.tolk @@ -14,13 +14,19 @@ fun getTwo(): X { return 2 as X; } fun takeInt(a: int) { return a; } @method_id(102) -fun test102(): (int, int, int, [(int, int)]) { +fun test102(): (int, int, int, [int, int]) { var a: int = getTwo(); var _: int = getTwo(); var b = getTwo() as int; var c: int = 1 ? getTwo() : getTwo(); var c redef = getTwo(); - return (eq1(a), eq2(b), takeInt(getTwo()), [(getTwo(), getTwo())]); + var ab_tens = (0, (1, 2)); + ab_tens.0 = getTwo(); + ab_tens.1.1 = getTwo(); + var ab_tup = [0, [1, 2]]; + ab_tup.0 = getTwo(); + ab_tup.1.1 = getTwo(); + return (eq1(a), eq2(b), takeInt(getTwo()), [getTwo(), ab_tens.1.1]); } @method_id(103) @@ -43,9 +49,9 @@ fun manyEq(a: T1, b: T2, c: T3): [T1, T2, T3] { fun test104(f: int) { var result = ( manyEq(1 ? 1 : 1, f ? 0 : null, !f ? getTwo() as int : null), - manyEq((f ? null as int : eq2(2), beginCell().storeBool(true).endCell().beginParse().loadBool()), 0, eq4(f)) + manyEq(f ? null as int : eq2(2), beginCell().storeBool(true).endCell().beginParse().loadBool(), eq4(f)) ); - __expect_type(result, "([int, int, int], [(int, bool), int, int])"); + __expect_type(result, "([int, int, int], [int, bool, int])"); return result; } @@ -74,7 +80,8 @@ fun test106() { return [ abstractTransform(cellToSlice, calcLoad32, c), abstractTransform(calcYPlus1, calcYPlus1, 0), - abstractTransform(calcTensorPlus1, calcTensorMul2, (2, 2)) + abstractTransform(calcTensorPlus1, calcTensorMul2, (2, 2)).0, + abstractTransform(calcTensorPlus1, calcTensorMul2, (2, 2)).1 ]; } @@ -135,7 +142,7 @@ fun main(x: int): (int, [[int, int]]) { @testcase | 101 | 0 | 0 0 0 [ 0 0 ] 0 0 0 [ 0 0 ] 0 0 0 [] @testcase | 102 | | 2 2 2 [ 2 2 ] @testcase | 103 | 0 | 0 100 100 -@testcase | 104 | 0 | [ 1 (null) 2 ] [ 2 -1 0 0 ] +@testcase | 104 | 0 | [ 1 (null) 2 ] [ 2 -1 0 ] @testcase | 105 | | 3 @testcase | 106 | | [ 106 2 6 6 ] @testcase | 107 | | 6 6 1 1 6 6 diff --git a/tolk-tester/tests/if_stmt.tolk b/tolk-tester/tests/if_stmt.tolk index 2c51ac515..0f78a5162 100644 --- a/tolk-tester/tests/if_stmt.tolk +++ b/tolk-tester/tests/if_stmt.tolk @@ -54,13 +54,13 @@ fun main() { test3 PROC:<{ // x DUP // x x - 20 NEQINT // x _2 + 20 NEQINT // x '2 IFNOTJMP:<{ // x DROP // - 20 PUSHINT // _3=20 + 20 PUSHINT // '3=20 }> // x DUP // x x - 50 EQINT // x _5 + 50 EQINT // x '5 IFNOTJMP:<{ // x """ */ diff --git a/tolk-tester/tests/indexed-access.tolk b/tolk-tester/tests/indexed-access.tolk new file mode 100644 index 000000000..38094fa5f --- /dev/null +++ b/tolk-tester/tests/indexed-access.tolk @@ -0,0 +1,287 @@ + +fun increment(mutate self: int) { + self += 1; +} + +fun increment2(mutate a: int, mutate b: int) { + a += 1; + b += 1; +} + +fun assign1020(mutate a: int, mutate b: int) { + a = 10; + b = 20; +} + +fun plus(mutate self: int, y: int): int { + val newVals = (self + y, y * 10); + self = newVals.0; + return newVals.1; +} + +fun eq(v: X): X { return v; } + +@method_id(101) +fun test101() { + var t = (1, (2, 3), [4, 5, [6, 7]], 8); + (t.0, t.1.0, t.2.0) = (2, 3, 5); + t.3.increment(); + t.2.1 += (t.1.1 += 1) - t.1.1 + 1; + increment2(mutate t.2.2.0, mutate t.2.2.1); + return t; +} + +global t102: (int, (int, int), [int, int, [int, int]], int); + +@method_id(102) +fun test102() { + t102 = (1, (2, 3), [4, 5, [6, 7]], 8); + (t102.0, t102.1.0, t102.2.0) = (2, 3, 5); + t102.3.increment(); + t102.2.1 += (t102.1.1 += 1) - t102.1.1 + 1; + increment2(mutate t102.2.2.0, mutate t102.2.2.1); + return t102; +} + +global t103: (int, int); + +@method_id(103) +fun test103() { + t103 = (5, 5); + assign1020(mutate t103.0, mutate t103.1); + var t = (5, 5); + assign1020(mutate t.0, mutate t.1); + return (t103, t); +} + +global t104: [[int, int]]; + +@method_id(104) +fun test104() { + var m = [[5, 5]]; + (m.0.0, m.0.1) = (10, 20); + t104 = [[5, 5]]; + (t104.0.0, t104.0.1) = (10, 20); + return (t104, m); +} + +@method_id(105) +fun test105(x: int, y: int): (tuple, int, (int, int), int, int) { + var ab = (createEmptyTuple(), (x, y), tupleSize); + ab.0.tuplePush(1); + tuplePush(mutate ab.0, 2); + ab.1.0 = null; + ab.1.1 += 10; + var cb = ab.2; + return (ab.0, ab.0.1, ab.1, cb(ab.0), ab.2(ab.0)); +} + +@method_id(106) +fun test106(x: int, y: int) { + var ab = [createEmptyTuple(), [x, y], tupleSize]; + ab.0.tuplePush(1); + tuplePush(mutate ab.0, 2); + ab.1.0 = null; + ab.1.1 += 10; + var cb = ab.2; + return (ab.0, ab.1, cb(ab.0), ab.2(ab.0)); +} + +@method_id(107) +fun test107() { + var ab = createEmptyTuple(); + ab.tuplePush(1); + ab.tuplePush(beginCell().storeInt(1, 32)); + return (ab.0 as int, getBuilderBitsCount(ab.1)); +} + +global t108: [int, [int, [int]]]; + +@method_id(108) +fun test108(last: int) { + t108 = [1, [2, [last]]]; + t108.1.1.0.increment(); + var t = [1, [2, [last]]]; + t.1.1.0.increment(); + return (t108, t); +} + +@method_id(109) +fun test109(x: (int, int)): (int, int, int, int, int, int, int) { + return (x.1, x.1.plus(x.1 / 20), x.1, x.1 = x.1 * 2, x.1, x.1 += 1, x.1); +} + +@method_id(110) +fun test110(f: int, s: int) { + var x = [f, s]; + return (x, x.1, x.1.plus(x.1 / 20), x.1, x.1 = x.1 * 2, x.1, x.1 += 1, x.1, x); +} + +global xx: (int, int); + +@method_id(111) +fun test111(x: (int, int)) { + xx = x; + return (x, xx.1, xx.1.plus(xx.1 / 20), eq(xx.1 += (x.1 *= 0)), xx.1 = xx.1 * 2, xx.1, xx.1 += 1, xx.1, x); +} + +global yy: [int, int]; + +@method_id(112) +fun test112(f: int, s: int) { + yy = [f, s]; + return (yy, yy.1, yy.1.plus(yy.1 / 20), eq(yy.1 += (yy.1 *= 0)), yy.1 = yy.1 * 2, yy.1, yy.1 += 1, yy.1, yy); +} + +@pure +fun getConstTuple() { + return [1,2]; +} + +fun testCodegenNoPureIndexedAccess() { + (getConstTuple().1, getConstTuple().0) = (3, 4); + return 0; +} + +@method_id(113) +fun test113() { + var x = [[1, 2]]; + return (x, x.0, plus(mutate x.0.0, 10), x.0, x, x.0 = [10, 20], x); +} + +@method_id(114) +fun test114(f: int, s: int) { + var x = ((), (f, s), ()); + return (x, x.1, plus(mutate x.1.0, 10), x.1, x, x.1 = (10, 20), x); +} + +@method_id(115) +fun test115() { + var y = [[[[true]]]]; + return (y, y.0.0.0.0 = !y.0.0.0.0, y.0); +} + +@method_id(116) +fun test116() { + var t = createEmptyTuple(); + t.tuplePush(1); + try { + return t.100500 as int; + } catch(excNo) { + return excNo; + } +} + +@method_id(117) +fun test117() { + var t = createEmptyTuple(); + t.tuplePush(1); + try { + return (t.0 as tuple).0 as int; + } catch(excNo) { + return excNo; + } +} + +@method_id(118) +fun testCodegenIndexPostfix1(x: (int, int)) { + var ab = (x.1, x.0); + return ab; +} + +@method_id(119) +fun testCodegenIndexPostfix2(x: (int, (int, int), int)) { + var y = x; + return (y.2, y.0, y.1.1); +} + +fun getT() { return (1, 2); } + +@method_id(120) +fun test120() { + return (getT().0 = 3, getT().0 = 4, [getT().0 = 5, getT().0 = 6]); +} + +@method_id(121) +fun test121(zero: int) { + var t = createEmptyTuple(); + t.tuplePush(-100); + t.tupleSetAt(0, zero); + (t.0 as int).increment(); + (((t.0) as int) as int).increment(); + increment(mutate t.0 as int); + return t; +} + +fun main(){} + + +/** +@testcase | 101 | | 2 3 4 [ 5 6 [ 7 8 ] ] 9 +@testcase | 102 | | 2 3 4 [ 5 6 [ 7 8 ] ] 9 +@testcase | 103 | | 10 20 10 20 +@testcase | 104 | | [ [ 10 20 ] ] [ [ 10 20 ] ] +@testcase | 105 | 5 6 | [ 1 2 ] 2 (null) 16 2 2 +@testcase | 106 | 5 6 | [ 1 2 ] [ (null) 16 ] 2 2 +@testcase | 107 | | 1 32 +@testcase | 108 | 3 | [ 1 [ 2 [ 4 ] ] ] [ 1 [ 2 [ 4 ] ] ] +@testcase | 109 | 0 100 | 100 50 105 210 210 211 211 +@testcase | 110 | 0 100 | [ 0 100 ] 100 50 105 210 210 211 211 [ 0 211 ] +@testcase | 111 | 0 100 | 0 100 100 50 105 210 210 211 211 0 0 +@testcase | 112 | 0 100 | [ 0 100 ] 100 50 105 210 210 211 211 [ 0 211 ] +@testcase | 113 | | [ [ 1 2 ] ] [ 1 2 ] 100 [ 11 2 ] [ [ 11 2 ] ] [ 10 20 ] [ [ 10 20 ] ] +@testcase | 114 | 1 2 | 1 2 1 2 100 11 2 11 2 10 20 10 20 +@testcase | 115 | | [ [ [ [ -1 ] ] ] ] 0 [ [ [ 0 ] ] ] +@testcase | 116 | | 5 +@testcase | 117 | | 7 +@testcase | 118 | 1 2 | 2 1 +@testcase | 119 | 1 2 3 4 | 4 1 3 +@testcase | 120 | | 3 4 [ 5 6 ] +@testcase | 121 | 0 | [ 3 ] + +@fif_codegen +""" + testCodegenNoPureIndexedAccess PROC:<{ + // + 0 PUSHINT // '8=0 + }> +""" + +@fif_codegen +""" + test104 PROC:<{ + // + 5 PUSHINT // '2=5 + DUP // '2=5 '3=5 + PAIR // '1 + SINGLE // m + 10 PUSHINT // m '5=10 + 20 PUSHINT // m '5=10 '6=20 + s2 PUSH // m '5=10 '6=20 m + 0 INDEX // m '10=10 '12=20 '8 + SWAP // m '10=10 '8 '12=20 + 1 SETINDEX // m '10=10 '8 + SWAP // m '8 '10=10 + 0 SETINDEX // m '8 + 0 SETINDEX // m + ... +""" + +@fif_codegen +""" + testCodegenIndexPostfix1 PROC:<{ + // x.0 x.1 + // ab.1 ab.0 + SWAP // ab.0 ab.1 + }> +""" + +@fif_codegen +""" + testCodegenIndexPostfix2 PROC:<{ + // x.0 x.1.0 x.1.1 x.2 + s2 POP // y.0 y.2 y.1.1 + s1 s2 XCHG // y.2 y.0 y.1.1 + }> +""" + */ diff --git a/tolk-tester/tests/invalid-assign-1.tolk b/tolk-tester/tests/invalid-assign-1.tolk new file mode 100644 index 000000000..f605056ee --- /dev/null +++ b/tolk-tester/tests/invalid-assign-1.tolk @@ -0,0 +1,9 @@ +fun main() { + var c = 1; + (c, c) = (2, 3); +} + +/** +@compilation_should_fail +@stderr one variable modified twice inside the same expression +*/ diff --git a/tolk-tester/tests/invalid-assign-2.tolk b/tolk-tester/tests/invalid-assign-2.tolk new file mode 100644 index 000000000..2838ed9ac --- /dev/null +++ b/tolk-tester/tests/invalid-assign-2.tolk @@ -0,0 +1,11 @@ +fun incThree(mutate a: int, mutate b: int, mutate c: int) {} + +fun main() { + var c = [[[1, 2]]]; + incThree(mutate c.0.0.0, mutate c.0.0.1, mutate c.0.0.0); +} + +/** +@compilation_should_fail +@stderr one variable modified twice inside the same expression +*/ diff --git a/tolk-tester/tests/invalid-assign-3.tolk b/tolk-tester/tests/invalid-assign-3.tolk new file mode 100644 index 000000000..d3f5d1f1a --- /dev/null +++ b/tolk-tester/tests/invalid-assign-3.tolk @@ -0,0 +1,10 @@ +global gg: (int, int); + +fun main() { + [gg.0, gg.1, gg.0] = [0, 1, 0]; +} + +/** +@compilation_should_fail +@stderr one variable modified twice inside the same expression +*/ diff --git a/tolk-tester/tests/invalid-assign-4.tolk b/tolk-tester/tests/invalid-assign-4.tolk new file mode 100644 index 000000000..67340b20b --- /dev/null +++ b/tolk-tester/tests/invalid-assign-4.tolk @@ -0,0 +1,10 @@ +global gg: (int, [int, int]); + +fun main() { + (gg.1.0, gg.1, gg.1.1) = (0, [1, 2], 3); +} + +/** +@compilation_should_fail +@stderr one variable both modified and read inside the same expression +*/ diff --git a/tolk-tester/tests/invalid-assign-5.tolk b/tolk-tester/tests/invalid-assign-5.tolk new file mode 100644 index 000000000..f3fe59f73 --- /dev/null +++ b/tolk-tester/tests/invalid-assign-5.tolk @@ -0,0 +1,9 @@ +fun main() { + var ab = (1, 2); + (ab, ab.1) = ((2, 3), 4); +} + +/** +@compilation_should_fail +@stderr one variable both modified and read inside the same expression +*/ diff --git a/tolk-tester/tests/invalid-assign-6.tolk b/tolk-tester/tests/invalid-assign-6.tolk new file mode 100644 index 000000000..59d769e96 --- /dev/null +++ b/tolk-tester/tests/invalid-assign-6.tolk @@ -0,0 +1,9 @@ +fun main() { + var t = createEmptyTuple(); + t.0 = (1, 2); +} + +/** +@compilation_should_fail +@stderr can not put `(int, int)` into a tuple, because it occupies 2 stack slots in TVM, not 1 +*/ diff --git a/tolk-tester/tests/invalid-assign-7.tolk b/tolk-tester/tests/invalid-assign-7.tolk new file mode 100644 index 000000000..6a33e6968 --- /dev/null +++ b/tolk-tester/tests/invalid-assign-7.tolk @@ -0,0 +1,8 @@ +fun main(cs: slice) { + var cb = cs.tupleSize; +} + +/** +@compilation_should_fail +@stderr referencing a method for `tuple` with object of type `slice` +*/ diff --git a/tolk-tester/tests/invalid-call-1.tolk b/tolk-tester/tests/invalid-call-1.tolk index 3542f5809..7435bb3cf 100644 --- a/tolk-tester/tests/invalid-call-1.tolk +++ b/tolk-tester/tests/invalid-call-1.tolk @@ -6,5 +6,5 @@ fun main(x: int) { /** @compilation_should_fail -@stderr calling a non-function +@stderr non-existing method `asdf` of type `int` */ diff --git a/tolk-tester/tests/invalid-call-10.tolk b/tolk-tester/tests/invalid-call-10.tolk new file mode 100644 index 000000000..9a28c0043 --- /dev/null +++ b/tolk-tester/tests/invalid-call-10.tolk @@ -0,0 +1,11 @@ +fun takeInvalidTuple(t: [int, (int, builder), int]) { +} + +fun main() { + takeInvalidTuple([1, (2, beginCell()), 0]); +} + +/** +@compilation_should_fail +@stderr can not put `(int, builder)` into a tuple, because it occupies 2 stack slots in TVM, not 1 + */ diff --git a/tolk-tester/tests/invalid-call-11.tolk b/tolk-tester/tests/invalid-call-11.tolk new file mode 100644 index 000000000..f631c546c --- /dev/null +++ b/tolk-tester/tests/invalid-call-11.tolk @@ -0,0 +1,11 @@ +fun main() { + var functions = (beginCell, beginCell); + var b = functions.1(); // ok + var c = functions.2(); // error +} + +/** +@compilation_should_fail +@stderr invalid tensor index, expected 0..1 +@stderr functions.2() + */ diff --git a/tolk-tester/tests/invalid-call-7.tolk b/tolk-tester/tests/invalid-call-7.tolk index 4ad038c9e..cf8c788c1 100644 --- a/tolk-tester/tests/invalid-call-7.tolk +++ b/tolk-tester/tests/invalid-call-7.tolk @@ -9,6 +9,6 @@ fun main() { /** @compilation_should_fail -@stderr undefined symbol `storeUnexisting` +@stderr non-existing method `storeUnexisting` of type `builder` @stderr .storeUnexisting() */ diff --git a/tolk-tester/tests/invalid-call-8.tolk b/tolk-tester/tests/invalid-call-8.tolk index c613d7d9c..199aa681a 100644 --- a/tolk-tester/tests/invalid-call-8.tolk +++ b/tolk-tester/tests/invalid-call-8.tolk @@ -1,8 +1,10 @@ +fun get_incoming_value() { return 3; } + fun main() { var incoming_ton: int = get_incoming_value().3(); } /** @compilation_should_fail -@stderr expected method name, got `3` +@stderr type `int` is not indexable */ diff --git a/tolk-tester/tests/invalid-generics-12.tolk b/tolk-tester/tests/invalid-generics-12.tolk new file mode 100644 index 000000000..62a6f5da4 --- /dev/null +++ b/tolk-tester/tests/invalid-generics-12.tolk @@ -0,0 +1,15 @@ +fun getTwo(): X { return 2; } + +fun cantDeduceNonArgumentGeneric() { + var t1: [int] = [0]; + t1.0 = getTwo(); // ok + var t2 = createEmptyTuple(); + t2.tuplePush(0); + t2.0 = getTwo(); // error, can't decude X +} + +/** +@compilation_should_fail +@stderr can not deduce X for generic function `getTwo` +@stderr t2.0 = getTwo(); + */ diff --git a/tolk-tester/tests/invalid-typing-13.tolk b/tolk-tester/tests/invalid-typing-13.tolk new file mode 100644 index 000000000..e356d0f33 --- /dev/null +++ b/tolk-tester/tests/invalid-typing-13.tolk @@ -0,0 +1,9 @@ +fun failAssignToInvalidTupleIndex() { + var ab = [1, 2]; + ab.100500 = 5; +} + +/** +@compilation_should_fail +@stderr invalid tuple index, expected 0..1 + */ diff --git a/tolk-tester/tests/known-bugs.tolk b/tolk-tester/tests/known-bugs.tolk deleted file mode 100644 index 4de6a3752..000000000 --- a/tolk-tester/tests/known-bugs.tolk +++ /dev/null @@ -1,27 +0,0 @@ -fun increment(mutate x: int): int { - x = x + 1; - return x; -} - -@method_id(101) -fun bugWithModifyingMethodInsideSameExpression() { - /* - The same bug existed in FunC: -#pragma allow-post-modification; -(int, int) ~increment(int x) { x = x + 5; return (x, x); } -int main() { int x = 0; x += x~increment(); return x; } - It's related to using a variable modified by ~method inside the same expression. - */ - var x = 0; - x = x + increment(mutate x); - return x; -} - -fun main() { - -} - -/** -// correct: 2 -@testcase | 101 | | 1 - */ diff --git a/tolk-tester/tests/logical-operators.tolk b/tolk-tester/tests/logical-operators.tolk index 9ef4858dc..29cd1d104 100644 --- a/tolk-tester/tests/logical-operators.tolk +++ b/tolk-tester/tests/logical-operators.tolk @@ -210,15 +210,15 @@ fun main() { compileTimeEval1 PROC:<{ // x DUP // x x - 0 EQINT // x _1 - FALSE // x _1 _4 - TRUE // x _1 _4 _7 - FALSE // x _1 _4 _7 _11 - s0 s4 XCHG // _11 _1 _4 _7 x - 0 EQINT // _11 _1 _4 _7 _12 - -10 EQINT // _11 _1 _4 _7 _14 + 0 EQINT // x '1 + FALSE // x '1 '4 + TRUE // x '1 '4 '7 + FALSE // x '1 '4 '7 '11 + s0 s4 XCHG // '11 '1 '4 '7 x + 0 EQINT // '11 '1 '4 '7 '12 + -10 EQINT // '11 '1 '4 '7 '14 s3 s4 XCHG - s1 s3 s0 XCHG3 // _1 _4 _7 _11 _14 + s1 s3 s0 XCHG3 // '1 '4 '7 '11 '14 }> """ @@ -230,15 +230,15 @@ fun main() { OVER // x y x IFNOTJMP:<{ // x y 2DROP // - 10 PUSHINT // _2=10 + 10 PUSHINT // '2=10 }> // x y DUP // x y y IFNOTJMP:<{ // x y 2DROP // - 20 PUSHINT // _3=20 + 20 PUSHINT // '3=20 RETALT }> // x y - ADD // _4 + ADD // '4 }> """ @@ -297,10 +297,10 @@ These are moments of future optimizations. For now, it's more than enough. // a b SWAP // b a IF:<{ // b - 0 NEQINT // _2 + 0 NEQINT // '2 }>ELSE<{ // b DROP // - 0 PUSHINT // _2=0 + 0 PUSHINT // '2=0 }> }> """ @@ -310,13 +310,13 @@ These are moments of future optimizations. For now, it's more than enough. testOrSimpleCodegen PROC:<{ // a b SWAP // b a - 0 GTINT // b _3 + 0 GTINT // b '3 IF:<{ // b DROP // - -1 PUSHINT // _4=-1 + -1 PUSHINT // '4=-1 }>ELSE<{ // b - 0 GTINT // _7 - 0 NEQINT // _4 + 0 GTINT // '7 + 0 NEQINT // '4 }> }> """ @@ -332,15 +332,15 @@ These are moments of future optimizations. For now, it's more than enough. DUP // x x IFNOTJMP:<{ // x DROP // - 1 PUSHINT // _5=1 + 1 PUSHINT // '5=1 }> // x DUP // x x IFNOTJMP:<{ // x DROP // - 1 PUSHINT // _6=1 + 1 PUSHINT // '6=1 }> // x 100 THROWIFNOT - -4 PUSHINT // _9=-4 + -4 PUSHINT // '9=-4 }> """ diff --git a/tolk-tester/tests/mutate-methods.tolk b/tolk-tester/tests/mutate-methods.tolk index 0a5ca1915..ebd07aca7 100644 --- a/tolk-tester/tests/mutate-methods.tolk +++ b/tolk-tester/tests/mutate-methods.tolk @@ -307,7 +307,7 @@ fun main(){} ... incrementTwoInPlace CALLDICT // x y sum1 -ROT - 10 PUSHINT // sum1 x y _10=10 + 10 PUSHINT // sum1 x y '10=10 incrementTwoInPlace CALLDICT // sum1 x y sum2 s1 s3 s0 XCHG3 // x y sum1 sum2 }> @@ -317,8 +317,8 @@ fun main(){} """ load_next PROC:<{ // cs - 32 LDI // _4 cs - SWAP // cs _4 + 32 LDI // '4 cs + SWAP // cs '4 }> """ @@ -326,7 +326,7 @@ fun main(){} """ testStoreUintPureUnusedResult PROC:<{ // - 0 PUSHINT // _11=0 + 0 PUSHINT // '11=0 }> """ @@ -335,9 +335,9 @@ fun main(){} testStoreUintImpureUnusedResult PROC:<{ // NEWC // b - STIX // _2 + STIX // '2 DROP // - 0 PUSHINT // _11=0 + 0 PUSHINT // '11=0 }> """ diff --git a/tolk-tester/tests/no-spaces.tolk b/tolk-tester/tests/no-spaces.tolk index da7338989..409bb342c 100644 --- a/tolk-tester/tests/no-spaces.tolk +++ b/tolk-tester/tests/no-spaces.tolk @@ -104,10 +104,10 @@ fun`main`(){} DUP // fst1=-1 snd1=-1 2 PUSHINT // fst1=-1 snd1=-1 trd1=2 s1 s1 s0 PUSH3 // fst1=-1 snd1=-1 trd1=2 fst2=-1 snd2=-1 trd2=2 - add3 CALLDICT // fst1=-1 snd1=-1 trd1=2 _13 - 3 -ROLL // _13 fst1=-1 snd1=-1 trd1=2 - add3 CALLDICT // _13 _14 - PAIR // _12 + add3 CALLDICT // fst1=-1 snd1=-1 trd1=2 '13 + 3 -ROLL // '13 fst1=-1 snd1=-1 trd1=2 + add3 CALLDICT // '13 '14 + PAIR // '12 }> """ diff --git a/tolk-tester/tests/null-keyword.tolk b/tolk-tester/tests/null-keyword.tolk index 40e391c8b..69678434d 100644 --- a/tolk-tester/tests/null-keyword.tolk +++ b/tolk-tester/tests/null-keyword.tolk @@ -99,21 +99,21 @@ fun main() { test1 PROC:<{ // PUSHNULL // numbers - 1 PUSHINT // numbers _2=1 - SWAP // _2=1 numbers + 1 PUSHINT // numbers '2=1 + SWAP // '2=1 numbers CONS // numbers - 2 PUSHINT // numbers _4=2 - SWAP // _4=2 numbers + 2 PUSHINT // numbers '4=2 + SWAP // '4=2 numbers CONS // numbers - 3 PUSHINT // numbers _6=3 - SWAP // _6=3 numbers + 3 PUSHINT // numbers '6=3 + SWAP // '6=3 numbers CONS // numbers - 4 PUSHINT // numbers _8=4 - SWAP // _8=4 numbers + 4 PUSHINT // numbers '8=4 + SWAP // '8=4 numbers CONS // numbers UNCONS // h numbers DUP // h numbers numbers - CAR // h numbers _13 + CAR // h numbers '13 """ @fif_codegen @@ -121,11 +121,11 @@ fun main() { main PROC:<{ // PUSHNULL // i - ISNULL // _2 + ISNULL // '2 IFJMP:<{ // - 1 PUSHINT // _3=1 + 1 PUSHINT // '3=1 }> // - 10 PUSHINT // _4=10 + 10 PUSHINT // '4=10 }> """ @@ -133,14 +133,14 @@ fun main() { """ test7 PROC:<{ ... - LDOPTREF // b _8 _7 + LDOPTREF // b '8 '7 DROP // b c - ISNULL // b _11 - 10 MULCONST // b _13 - SWAP // _13 b - ISNULL // _13 _14 - NOT // _13 _15 - ADD // _16 + ISNULL // b '11 + 10 MULCONST // b '13 + SWAP // '13 b + ISNULL // '13 '14 + NOT // '13 '15 + ADD // '16 }> """ */ diff --git a/tolk-tester/tests/op-priority.tolk b/tolk-tester/tests/op-priority.tolk index 8a57b3940..e3c6bb6e8 100644 --- a/tolk-tester/tests/op-priority.tolk +++ b/tolk-tester/tests/op-priority.tolk @@ -95,26 +95,26 @@ fun main() { unary_minus_1 PROC:<{ // a b c -ROT // c a b - ADD // c _3 - NEGATE // c _4 - SWAP // _4 c - MUL // _5 + ADD // c '3 + NEGATE // c '4 + SWAP // '4 c + MUL // '5 }> unary_minus_2 PROC:<{ // a b c -ROT // c a b - ADD // c _3 - NEGATE // c _4 - SWAP // _4 c - MUL // _5 + ADD // c '3 + NEGATE // c '4 + SWAP // '4 c + MUL // '5 }> unary_minus_3 PROC:<{ // a b c -ROT // c a b - ADD // c _3 - SWAP // _3 c - MUL // _4 - NEGATE // _5 + ADD // c '3 + SWAP // '3 c + MUL // '4 + NEGATE // '5 }> """ diff --git a/tolk-tester/tests/use-before-declare.tolk b/tolk-tester/tests/use-before-declare.tolk index 384569b93..d3e6b165c 100644 --- a/tolk-tester/tests/use-before-declare.tolk +++ b/tolk-tester/tests/use-before-declare.tolk @@ -43,7 +43,7 @@ const demo_20: int = 20; """ test1 PROC:<{ // - 30 PUSHINT // _10 + 30 PUSHINT // '10 }> """ */ diff --git a/tolk-tester/tests/var-apply.tolk b/tolk-tester/tests/var-apply.tolk index a0918c181..168635602 100644 --- a/tolk-tester/tests/var-apply.tolk +++ b/tolk-tester/tests/var-apply.tolk @@ -129,6 +129,15 @@ fun testVarApply3() { return (getIntAt(t, 0), getTupleFirstInt(t), getTupleLastTuple(t), getTupleLastGetter()(t)); } +@method_id(107) +fun testIndexedAccessApply() { + var functions1 = (beginCell, endCell); + var functions2 = [beginParse]; + var b = functions1.0().storeInt(1, 16); + b.storeInt(1, 16); + return functions2.0(functions1.1(b)).loadInt(32); +} + fun main() {} /** @@ -138,4 +147,5 @@ fun main() {} @testcase | 104 | | 240 @testcase | 105 | | 1 @testcase | 106 | | 1 1 [ 2 ] [ 2 ] +@testcase | 107 | | 65537 */ diff --git a/tolk/abscode.cpp b/tolk/abscode.cpp index 25ddd10ef..b465b72b4 100644 --- a/tolk/abscode.cpp +++ b/tolk/abscode.cpp @@ -26,22 +26,28 @@ namespace tolk { * */ -void TmpVar::dump(std::ostream& os) const { - show(os); - os << " : " << v_type << " (width "; - os << v_type->calc_width_on_stack(); - os << ")"; - os << std::endl; +void TmpVar::show_as_stack_comment(std::ostream& os) const { + if (!name.empty()) { + os << name; + } else { + os << '\'' << ir_idx; + } +#ifdef TOLK_DEBUG + // uncomment for detailed stack output, like `'15(binary-op) '16(glob-var)` + // if (desc) os << desc; +#endif } -void TmpVar::show(std::ostream& os, int omit_idx) const { - if (v_sym) { - os << v_sym->name; - if (omit_idx >= 2) { - return; - } +void TmpVar::show(std::ostream& os) const { + os << '\'' << ir_idx; // vars are printed out as `'1 '2` (in stack comments, debug info, etc.) + if (!name.empty()) { + os << '_' << name; + } +#ifdef TOLK_DEBUG + if (desc) { + os << ' ' << desc; // "origin" of implicitly created tmp var, like `'15 (binary-op) '16 (glob-var)` } - os << '_' << ir_idx; +#endif } std::ostream& operator<<(std::ostream& os, const TmpVar& var) { @@ -95,7 +101,7 @@ void VarDescr::show(std::ostream& os, const char* name) const { if (name) { os << name; } - os << '_' << idx; + os << '\'' << idx; show_value(os); } @@ -333,7 +339,7 @@ void Op::show_var_list(std::ostream& os, const std::vector& idx_list, } 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 << ", " << vars.at(idx_list[i]); } os << ")"; } @@ -378,11 +384,12 @@ 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.is_defined() && (flags & 1) != 0) { - var.where.show(os); + var.show(os); + os << " : " << var.v_type << std::endl; + if (var.loc.is_defined() && (flags & 1) != 0) { + var.loc.show(os); os << " defined here:\n"; - var.where.show_context(os); + var.loc.show_context(os); } } } @@ -393,21 +400,25 @@ void CodeBlob::print(std::ostream& os, int flags) const { os << "-------- END ---------\n\n"; } -std::vector CodeBlob::create_var(TypePtr var_type, const LocalVarData* v_sym, SrcLocation loc) { +std::vector CodeBlob::create_var(TypePtr var_type, SrcLocation loc, std::string name) { std::vector ir_idx; - ir_idx.reserve(var_type->calc_width_on_stack()); + int stack_w = var_type->calc_width_on_stack(); + ir_idx.reserve(stack_w); if (const TypeDataTensor* t_tensor = var_type->try_as()) { - for (TypePtr item : t_tensor->items) { - std::vector nested = create_var(item, v_sym, loc); + for (int i = 0; i < t_tensor->size(); ++i) { + std::string sub_name = name.empty() ? name : name + "." + std::to_string(i); + std::vector nested = create_var(t_tensor->items[i], loc, std::move(sub_name)); ir_idx.insert(ir_idx.end(), nested.begin(), nested.end()); } } else if (var_type != TypeDataVoid::create()) { - tolk_assert(var_type->calc_width_on_stack() == 1); - vars.emplace_back(var_cnt, var_type, v_sym, loc); +#ifdef TOLK_DEBUG + tolk_assert(stack_w == 1); +#endif + vars.emplace_back(var_cnt, var_type, std::move(name), loc); ir_idx.emplace_back(var_cnt); var_cnt++; } - tolk_assert(static_cast(ir_idx.size()) == var_type->calc_width_on_stack()); + tolk_assert(static_cast(ir_idx.size()) == stack_w); return ir_idx; } diff --git a/tolk/asmops.cpp b/tolk/asmops.cpp index 547922dad..2618ed26e 100644 --- a/tolk/asmops.cpp +++ b/tolk/asmops.cpp @@ -302,24 +302,13 @@ Const AsmOpList::get_const(const_idx_t idx) { } } -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; + var_idx_t i = idx_pair.first; + const_idx_t j = idx_pair.second; if (!var_names_ || (unsigned)i >= var_names_->size()) { - os << '_' << i; + os << '\'' << i; } else { - var_names_->at(i).show(os, 2); - // if (!var_names_->at(i).v_type->is_int()) { - // os << '<'; var_names_->at(i).v_type->print(os); os << '>'; - // } + var_names_->at(i).show_as_stack_comment(os); } if ((unsigned)j < constants_.size() && constants_[j].not_null()) { os << '=' << constants_[j]; diff --git a/tolk/ast-from-tokens.cpp b/tolk/ast-from-tokens.cpp index 58592011e..3eb4385aa 100644 --- a/tolk/ast-from-tokens.cpp +++ b/tolk/ast-from-tokens.cpp @@ -405,12 +405,15 @@ static AnyExprV parse_expr80(Lexer& lex) { lex.next(); V v_ident = nullptr; V v_instantiationTs = nullptr; - if (lex.tok() == tok_identifier) { + if (lex.tok() == tok_identifier) { // obj.field / obj.method v_ident = createV(lex.cur_location(), lex.cur_str()); lex.next(); if (lex.tok() == tok_lt) { v_instantiationTs = parse_maybe_instantiationTs_after_identifier(lex); } + } else if (lex.tok() == tok_int_const) { // obj.0 (indexed access) + v_ident = createV(lex.cur_location(), lex.cur_str()); + lex.next(); } else { lex.unexpected("method name"); } diff --git a/tolk/ast.h b/tolk/ast.h index b90507e7e..d2db49f81 100644 --- a/tolk/ast.h +++ b/tolk/ast.h @@ -529,8 +529,14 @@ struct Vertex final : ASTExprUnary { public: - typedef const FunctionData* DotTarget; // for `t.tupleAt` target is `tupleAt` global function - DotTarget target = nullptr; // filled at type inferring + typedef std::variant< + const FunctionData*, // for `t.tupleAt` target is `tupleAt` global function + int // for `t.0` target is "indexed access" 0 + > DotTarget; + DotTarget target = static_cast(nullptr); // filled at type inferring + + bool is_target_fun_ref() const { return std::holds_alternative(target); } + bool is_target_indexed_access() const { return std::holds_alternative(target); } AnyExprV get_obj() const { return child; } auto get_identifier() const { return identifier; } diff --git a/tolk/builtins.cpp b/tolk/builtins.cpp index 73aef2d99..2b207c253 100644 --- a/tolk/builtins.cpp +++ b/tolk/builtins.cpp @@ -1060,6 +1060,17 @@ AsmOp compile_tuple_at(std::vector& res, std::vector& args, return exec_op("INDEXVAR", 2, 1); } +// fun tupleSetAt(mutate self: tuple, value: X, index: int): void asm "SETINDEXVAR"; +AsmOp compile_tuple_set_at(std::vector& res, std::vector& args, SrcLocation) { + tolk_assert(args.size() == 3 && res.size() == 1); + auto& y = args[2]; + if (y.is_int_const() && y.int_const >= 0 && y.int_const < 16) { + y.unused(); + return exec_arg_op("SETINDEX", y.int_const, 1, 1); + } + return exec_op("SETINDEXVAR", 2, 1); +} + // fun __isNull(X arg): bool AsmOp compile_is_null(std::vector& res, std::vector& args, SrcLocation) { tolk_assert(args.size() == 1 && res.size() == 1); @@ -1246,6 +1257,9 @@ void define_builtins() { define_builtin_func("tupleAt", {Tuple, Int}, typeT, declGenericT, compile_tuple_at, FunctionData::flagMarkedAsPure | FunctionData::flagAcceptsSelf); + define_builtin_func("tupleSetAt", {Tuple, typeT, Int}, Unit, declGenericT, + compile_tuple_set_at, + FunctionData::flagMarkedAsPure | FunctionData::flagHasMutateParams | FunctionData::flagAcceptsSelf); define_builtin_func("debugPrint", {typeT}, Unit, declGenericT, AsmOp::Custom("s0 DUMP DROP", 1, 1), 0); diff --git a/tolk/codegen.cpp b/tolk/codegen.cpp index 3830f7ae5..ad61b8a53 100644 --- a/tolk/codegen.cpp +++ b/tolk/codegen.cpp @@ -132,7 +132,7 @@ int Stack::drop_vars_except(const VarDescrList& var_info, int excl_var) { return dropped; } -void Stack::show(int flags) { +void Stack::show() { std::ostringstream os; for (auto i : s) { os << ' '; diff --git a/tolk/pipe-ast-to-legacy.cpp b/tolk/pipe-ast-to-legacy.cpp index c64fe751e..f5eca22cc 100644 --- a/tolk/pipe-ast-to-legacy.cpp +++ b/tolk/pipe-ast-to-legacy.cpp @@ -21,6 +21,7 @@ #include "type-system.h" #include "common/refint.h" #include "constant-evaluator.h" +#include /* * This pipe is the last one operating AST: it transforms AST to IR. @@ -28,38 +29,218 @@ * kernel (initially forked from FunC) comes into play. * Up to this point, all types have been inferred, all validity checks have been passed, etc. * All properties in AST nodes are assigned and can be safely used (fun_ref, etc.). - * So, if execution reaches this pass, the input is correct, and code generation should succeed. + * So, if execution reaches this pass, the input is (almost) correct, and code generation should succeed. + * The only thing additionally checked during this pass is tricky lvalue, like one and the same variable + * assigned/mutated multiple times in same expression, e.g. `(t.0, t.0) = rhs` / `f(mutate x.1.2, mutate x)`. */ namespace tolk { -struct LValGlobs { - std::vector>> globs; +// fire error on cases like `(a, a) = rhs` / `f(mutate t.1.0, mutate t.1.0)` +GNU_ATTRIBUTE_NORETURN GNU_ATTRIBUTE_COLD +static void fire_error_variable_modified_twice_inside_same_expression(SrcLocation loc) { + throw ParseError(loc, "one variable modified twice inside the same expression"); +} - void add_modified_glob(const GlobalVarData* g_sym, std::vector local_ir_idx) { - globs.emplace_back(g_sym, std::move(local_ir_idx)); - } +// fire error on cases like `(m.1.0, m.1) = rhs` (m.1 inside m.1.0 is "rval inside lval") +GNU_ATTRIBUTE_NORETURN GNU_ATTRIBUTE_COLD +static void fire_error_variable_modified_and_read_inside_same_expression(SrcLocation loc) { + throw ParseError(loc, "one variable both modified and read inside the same expression"); +} - void gen_ops_set_globs(CodeBlob& code, SrcLocation loc) const { - for (const auto& [g_sym, ir_idx] : globs) { - Op& op = code.emplace_back(loc, Op::_SetGlob, std::vector{}, ir_idx, g_sym); +// Main goal of LValContext is to handle non-primitive lvalues. At IR level, a usual local variable +// exists, but on its change, something non-trivial should happen. +// Example: `globalVar = 9` actually does `Const $5 = 9` + `Let $6 = $5` + `SetGlob "globVar" = $6` +// Example: `tupleVar.0 = 9` actually does `Const $5 = 9` + `Let $6 = $5` + `Const $7 = 0` + `Call tupleSetAt($4, $6, $7)` +// Of course, mixing globals with tuples should also be supported. +// To achieve this, treat tupleObj inside "tupleObj.i" like "rvalue inside lvalue". +// For instance, `globalTuple.0 = 9` reads global (like rvalue), assigns 9 to tmp var, modifies tuple, writes global. +// A challenging thing is handling "unique" parts, to be read/updated only once. +// Example: `f(mutate globalTensor.0, mutate globalTensor.1)`, then globalTensor should be read/written once. +// Example: `(t.0.0, t.0.1) = rhs` (m is [[int, int]]), then t.0 should be read/updated once. +// Solving this by calculating hashes of every lvalue or rvalue inside lvalue automatically gives an ability +// to detect and fire "multiple writes inside expression", like `(a, a) = rhs` / `[t.0, (t.0.1, c)] = rhs`. +// Note, that tensors (not tuples) `tensorVar.0 = 9` do not emit anything special (unless global). +class LValContext { + // every global variable used as lvalue is registered here + // example: `globalInt = 9`, implicit var is created `$tmp = 9`, and `SetGlob "globalInt" $tmp` is done after + // global tensors are stored as tuples (unpacked on reading, packed on writing), then multiple tmp vars are created + struct ModifiedGlob { + const GlobalVarData* glob_ref; + std::vector local_ir_idx; // typically 1, generally calc_width_on_stack() of global var (tensors) + + void apply(CodeBlob& code, SrcLocation loc) const { + Op& op = code.emplace_back(loc, Op::_SetGlob, std::vector{}, local_ir_idx, glob_ref); op.set_impure_flag(); } + }; + + // every tuple index used as lvalue is registered here + // example: `t.0 = 9`, implicit var is created `$tmp = 9`, as well as `$tmp_idx = 0` and `tupleSetAt()` is done after + // for `t.0.0` if t is `[[int, ...]]`, `tupleAt()` for it is done since it's rvalue, and `tupleSetAt()` is done 2 times + struct ModifiedTupleIndex { + uint64_t hash; + var_idx_t tuple_ir_idx; + var_idx_t index_ir_idx; + var_idx_t field_ir_idx; + + void apply(CodeBlob& code, SrcLocation loc) const { + const FunctionData* builtin_sym = lookup_global_symbol("tupleSetAt")->as(); + code.emplace_back(loc, Op::_Call, std::vector{tuple_ir_idx}, std::vector{tuple_ir_idx, field_ir_idx, index_ir_idx}, builtin_sym); + } + }; + + int level_rval_inside_lval = 0; + std::vector> modifications; + std::unordered_set all_modified_hashes; + + void fire_if_one_variable_modified_twice(SrcLocation loc, uint64_t modified_hash) { + if (!is_rval_inside_lval()) { + if (!all_modified_hashes.insert(modified_hash).second) { + fire_error_variable_modified_twice_inside_same_expression(loc); + } + if (all_modified_hashes.contains(~modified_hash)) { + fire_error_variable_modified_and_read_inside_same_expression(loc); + } + } else { + all_modified_hashes.insert(~modified_hash); + if (all_modified_hashes.contains(modified_hash)) { + fire_error_variable_modified_and_read_inside_same_expression(loc); + } + } + } + +public: + void enter_rval_inside_lval() { level_rval_inside_lval++; } + void exit_rval_inside_lval() { level_rval_inside_lval--; } + bool is_rval_inside_lval() const { return level_rval_inside_lval > 0; } + + uint64_t register_lval(SrcLocation loc, const LocalVarData* var_ref) { + uint64_t hash = reinterpret_cast(var_ref); + fire_if_one_variable_modified_twice(loc, hash); + return hash; + } + + uint64_t register_lval(SrcLocation loc, const GlobalVarData* glob_ref) { + uint64_t hash = reinterpret_cast(glob_ref); + fire_if_one_variable_modified_twice(loc, hash); + return hash; + } + + uint64_t register_lval(SrcLocation loc, V v) { + uint64_t hash = 7; + AnyExprV leftmost_obj = v; + while (auto v_dot = leftmost_obj->try_as()) { + if (!v_dot->is_target_indexed_access()) { + break; + } + hash = hash * 1915239017 + std::get(v_dot->target); + leftmost_obj = v_dot->get_obj(); + } + if (auto v_ref = leftmost_obj->try_as()) { + hash *= reinterpret_cast(v_ref->sym); // `v.0` and `v.0` in 2 places is the same + } else { + hash *= reinterpret_cast(leftmost_obj); // unlike `f().0` and `f().0` (pointers to AST nodes differ) + } + fire_if_one_variable_modified_twice(loc, hash); + return hash; + } + + const std::vector* exists_already_known_global(const GlobalVarData* glob_ref) const { + for (const auto& m : modifications) { + if (const auto* m_glob = std::get_if(&m); m_glob && m_glob->glob_ref == glob_ref) { + return &m_glob->local_ir_idx; + } + } + return nullptr; + } + + const var_idx_t* exists_already_known_tuple_index(uint64_t hash) const { + for (const auto& m : modifications) { + if (const auto* m_tup = std::get_if(&m); m_tup && m_tup->hash == hash) { + return &m_tup->field_ir_idx; + } + } + return nullptr; + } + + void register_modified_global(const GlobalVarData* glob_ref, std::vector local_ir_idx) { + modifications.emplace_back(ModifiedGlob{glob_ref, std::move(local_ir_idx)}); + } + + void register_modified_tuple_index(uint64_t hash, var_idx_t tuple_ir_idx, var_idx_t index_ir_idx, var_idx_t field_ir_idx) { + modifications.emplace_back(ModifiedTupleIndex{hash, tuple_ir_idx, index_ir_idx, field_ir_idx}); + } + + void gen_ops_if_nonempty(CodeBlob& code, SrcLocation loc) const { + for (auto it = modifications.rbegin(); it != modifications.rend(); ++it) { // reverse, it's important + if (const auto* m_glob = std::get_if(&*it)) { + m_glob->apply(code, loc); + } else if (const auto* m_tup = std::get_if(&*it)) { + m_tup->apply(code, loc); + } + } + } +}; + +// The goal of VarsModificationWatcher is to detect such cases: `return (x, x += y, x)`. +// Without any changes, ops will be { _Call $2 = +($0_x, $1_y); _Return $0_x, $2, $0_x } - incorrect +// Correct will be to introduce tmp var: { _Let $3 = $0_x; _Call $2 = ...; _Return $3, $2, $0_x } +// This "introducing" is done when compiling tensors, whereas this class allows to watch vars for modification. +class VarsModificationWatcher { + struct WatchedVar { + var_idx_t ir_idx; + std::function on_modification_callback; + + WatchedVar(var_idx_t ir_idx, std::function on_modification_callback) + : ir_idx(ir_idx), on_modification_callback(std::move(on_modification_callback)) {} + }; + + std::vector all_callbacks; + +public: + + bool empty() const { return all_callbacks.empty(); } + + void push_callback(var_idx_t ir_idx, std::function callback) { + all_callbacks.emplace_back(ir_idx, std::move(callback)); + } + + void pop_callback(var_idx_t ir_idx) { + for (auto it = all_callbacks.rbegin(); it != all_callbacks.rend(); ++it) { + if (it->ir_idx == ir_idx) { + all_callbacks.erase((it + 1).base()); + return; + } + } + tolk_assert(false); + } + + void trigger_callbacks(const std::vector& left_lval_indices, SrcLocation loc) const { + for (const WatchedVar& w : all_callbacks) { + for (var_idx_t changed_var : left_lval_indices) { + if (w.ir_idx == changed_var) { + w.on_modification_callback(loc, w.ir_idx); + } + } + } } }; -std::vector pre_compile_expr(AnyExprV v, CodeBlob& code, LValGlobs* lval_globs = nullptr); +static VarsModificationWatcher vars_modification_watcher; + +std::vector pre_compile_expr(AnyExprV v, CodeBlob& code, LValContext* lval_ctx = nullptr); void process_any_statement(AnyV v, CodeBlob& code); static std::vector> pre_compile_tensor_inner(CodeBlob& code, const std::vector& args, - LValGlobs* lval_globs) { + LValContext* lval_ctx) { const int n = static_cast(args.size()); if (n == 0) { // just `()` return {}; } if (n == 1) { // just `(x)`: even if x is modified (e.g. `f(x=x+2)`), there are no next arguments - return {pre_compile_expr(args[0], code, lval_globs)}; + return {pre_compile_expr(args[0], code, lval_ctx)}; } // the purpose is to handle such cases: `return (x, x += y, x)` @@ -81,9 +262,9 @@ static std::vector> pre_compile_tensor_inner(CodeBlob& co void add_and_watch_modifications(std::vector&& vars_of_ith_arg, CodeBlob& code) { for (var_idx_t ir_idx : vars_of_ith_arg) { - if (code.vars[ir_idx].v_sym && !is_watched(ir_idx)) { + if (!code.vars[ir_idx].name.empty() && !is_watched(ir_idx)) { watched_vars.emplace_back(ir_idx); - code.vars[ir_idx].on_modification.emplace_back([this, &code, ir_idx](SrcLocation loc) { + vars_modification_watcher.push_callback(ir_idx, [this, &code](SrcLocation loc, var_idx_t ir_idx) { on_var_modified(ir_idx, loc, code); }); } @@ -93,7 +274,7 @@ static std::vector> pre_compile_tensor_inner(CodeBlob& co void on_var_modified(var_idx_t ir_idx, SrcLocation loc, CodeBlob& code) { tolk_assert(is_watched(ir_idx)); - std::vector tmp_idx_arr = code.create_tmp_var(code.vars[ir_idx].v_type, loc); + std::vector tmp_idx_arr = code.create_tmp_var(code.vars[ir_idx].v_type, loc, "(pre-modified)"); tolk_assert(tmp_idx_arr.size() == 1); var_idx_t tmp_idx = tmp_idx_arr[0]; code.emplace_back(loc, Op::_Let, std::vector{tmp_idx}, std::vector{ir_idx}); @@ -102,9 +283,9 @@ static std::vector> pre_compile_tensor_inner(CodeBlob& co } } - std::vector> clear_and_stop_watching(CodeBlob& code) { + std::vector> clear_and_stop_watching() { for (var_idx_t ir_idx : watched_vars) { - code.vars[ir_idx].on_modification.pop_back(); + vars_modification_watcher.pop_callback(ir_idx); } watched_vars.clear(); return std::move(res_lists); @@ -113,15 +294,15 @@ static std::vector> pre_compile_tensor_inner(CodeBlob& co WatchingVarList watched_vars(n); for (int arg_idx = 0; arg_idx < n; ++arg_idx) { - std::vector vars_of_ith_arg = pre_compile_expr(args[arg_idx], code, lval_globs); + std::vector vars_of_ith_arg = pre_compile_expr(args[arg_idx], code, lval_ctx); watched_vars.add_and_watch_modifications(std::move(vars_of_ith_arg), code); } - return watched_vars.clear_and_stop_watching(code); + return watched_vars.clear_and_stop_watching(); } static std::vector pre_compile_tensor(CodeBlob& code, const std::vector& args, - LValGlobs* lval_globs = nullptr) { - std::vector> res_lists = pre_compile_tensor_inner(code, args, lval_globs); + LValContext* lval_ctx = nullptr) { + std::vector> res_lists = pre_compile_tensor_inner(code, args, lval_ctx); std::vector res; for (const std::vector& list : res_lists) { res.insert(res.end(), list.cbegin(), list.cend()); @@ -133,11 +314,11 @@ static std::vector pre_compile_let(CodeBlob& code, AnyExprV lhs, AnyE // [lhs] = [rhs]; since type checking is ok, it's the same as "lhs = rhs" if (lhs->type == ast_typed_tuple && rhs->type == ast_typed_tuple) { std::vector right = pre_compile_tensor(code, rhs->as()->get_items()); - LValGlobs globs; - std::vector left = pre_compile_tensor(code, lhs->as()->get_items(), &globs); - code.on_var_modification(left, loc); + LValContext local_lval; + std::vector left = pre_compile_tensor(code, lhs->as()->get_items(), &local_lval); + vars_modification_watcher.trigger_callbacks(left, loc); code.emplace_back(loc, Op::_Let, std::move(left), right); - globs.gen_ops_set_globs(code, loc); + local_lval.gen_ops_if_nonempty(code, loc); return right; } // [lhs] = rhs; it's un-tuple to N left vars @@ -145,29 +326,37 @@ static std::vector pre_compile_let(CodeBlob& code, AnyExprV lhs, AnyE std::vector right = pre_compile_expr(rhs, code); const TypeDataTypedTuple* inferred_tuple = rhs->inferred_type->try_as(); std::vector types_list = inferred_tuple->items; - std::vector rvect = code.create_tmp_var(TypeDataTensor::create(std::move(types_list)), rhs->loc); + std::vector rvect = code.create_tmp_var(TypeDataTensor::create(std::move(types_list)), rhs->loc, "(unpack-tuple)"); code.emplace_back(lhs->loc, Op::_UnTuple, rvect, std::move(right)); - LValGlobs globs; - std::vector left = pre_compile_tensor(code, lhs->as()->get_items(), &globs); - code.on_var_modification(left, loc); + LValContext local_lval; + std::vector left = pre_compile_tensor(code, lhs->as()->get_items(), &local_lval); + vars_modification_watcher.trigger_callbacks(left, loc); code.emplace_back(loc, Op::_Let, std::move(left), rvect); - globs.gen_ops_set_globs(code, loc); + local_lval.gen_ops_if_nonempty(code, loc); return rvect; } + // small optimization: `var x = rhs` or `local_var = rhs` (90% cases), LValContext not needed actually + if (lhs->type == ast_local_var_lhs || (lhs->type == ast_reference && lhs->as()->sym->try_as())) { + std::vector right = pre_compile_expr(rhs, code); + std::vector left = pre_compile_expr(lhs, code); // effectively, local_var->ir_idx + vars_modification_watcher.trigger_callbacks(left, loc); + code.emplace_back(loc, Op::_Let, std::move(left), right); + return right; + } // lhs = rhs std::vector right = pre_compile_expr(rhs, code); - LValGlobs globs; - std::vector left = pre_compile_expr(lhs, code, &globs); - code.on_var_modification(left, loc); + LValContext local_lval; + std::vector left = pre_compile_expr(lhs, code, &local_lval); + vars_modification_watcher.trigger_callbacks(left, loc); code.emplace_back(loc, Op::_Let, std::move(left), right); - globs.gen_ops_set_globs(code, loc); + local_lval.gen_ops_if_nonempty(code, loc); return right; } -static std::vector gen_op_call(CodeBlob& code, TypePtr ret_type, SrcLocation here, - std::vector&& args_vars, const FunctionData* fun_ref) { - std::vector rvect = code.create_tmp_var(ret_type, here); - Op& op = code.emplace_back(here, Op::_Call, rvect, std::move(args_vars), fun_ref); +static std::vector gen_op_call(CodeBlob& code, TypePtr ret_type, SrcLocation loc, + std::vector&& args_vars, const FunctionData* fun_ref, const char* debug_desc) { + std::vector rvect = code.create_tmp_var(ret_type, loc, debug_desc); + Op& op = code.emplace_back(loc, Op::_Call, rvect, std::move(args_vars), fun_ref); if (!fun_ref->is_marked_as_pure()) { op.set_impure_flag(); } @@ -175,30 +364,42 @@ static std::vector gen_op_call(CodeBlob& code, TypePtr ret_type, SrcL } -static std::vector process_symbol(SrcLocation loc, const Symbol* sym, CodeBlob& code, LValGlobs* lval_globs) { +static std::vector pre_compile_symbol(SrcLocation loc, const Symbol* sym, CodeBlob& code, LValContext* lval_ctx) { if (const auto* glob_ref = sym->try_as()) { - std::vector rvect = code.create_tmp_var(glob_ref->declared_type, loc); - if (lval_globs) { - lval_globs->add_modified_glob(glob_ref, rvect); - return rvect; + if (!lval_ctx) { + // `globalVar` is used for reading, just create local IR var to represent its value, Op GlobVar will fill it + // note, that global tensors are stored as a tuple an unpacked to N vars on read, N determined by declared_type + std::vector local_ir_idx = code.create_tmp_var(glob_ref->declared_type, loc, "(glob-var)"); + code.emplace_back(loc, Op::_GlobVar, local_ir_idx, std::vector{}, glob_ref); + return local_ir_idx; } else { - code.emplace_back(loc, Op::_GlobVar, rvect, std::vector{}, glob_ref); - return rvect; + // `globalVar = rhs` / `mutate globalVar` / `globalTuple.0 = rhs` + lval_ctx->register_lval(loc, glob_ref); + if (const std::vector* local_ir_idx = lval_ctx->exists_already_known_global(glob_ref)) { + return *local_ir_idx; // `f(mutate g.0, mutate g.1)`, then g will be read only once + } + std::vector local_ir_idx = code.create_tmp_var(glob_ref->declared_type, loc, "(glob-var)"); + if (lval_ctx->is_rval_inside_lval()) { // for `globalVar.0` "globalVar" is rvalue inside lvalue + // for `globalVar = rhs` don't read a global actually, but for `globalVar.0 = rhs` do + code.emplace_back(loc, Op::_GlobVar, local_ir_idx, std::vector{}, glob_ref); + } + lval_ctx->register_modified_global(glob_ref, local_ir_idx); + return local_ir_idx; } } if (const auto* const_ref = sym->try_as()) { if (const_ref->is_int_const()) { - std::vector rvect = code.create_tmp_var(TypeDataInt::create(), loc); + std::vector rvect = code.create_tmp_var(TypeDataInt::create(), loc, "(glob-const)"); code.emplace_back(loc, Op::_IntConst, rvect, const_ref->as_int_const()); return rvect; } else { - std::vector rvect = code.create_tmp_var(TypeDataSlice::create(), loc); + std::vector rvect = code.create_tmp_var(TypeDataSlice::create(), loc, "(glob-const)"); code.emplace_back(loc, Op::_SliceConst, rvect, const_ref->as_slice_const()); return rvect; } } if (const auto* fun_ref = sym->try_as()) { - std::vector rvect = code.create_tmp_var(fun_ref->inferred_full_type, loc); + std::vector rvect = code.create_tmp_var(fun_ref->inferred_full_type, loc, "(glob-var-fun)"); code.emplace_back(loc, Op::_GlobVar, rvect, std::vector{}, fun_ref); return rvect; } @@ -206,9 +407,12 @@ static std::vector process_symbol(SrcLocation loc, const Symbol* sym, #ifdef TOLK_DEBUG tolk_assert(static_cast(var_ref->ir_idx.size()) == var_ref->declared_type->calc_width_on_stack()); #endif + if (lval_ctx) { + lval_ctx->register_lval(loc, var_ref); + } return var_ref->ir_idx; } - throw Fatal("process_symbol"); + throw Fatal("pre_compile_symbol"); } static std::vector process_assign(V v, CodeBlob& code) { @@ -234,7 +438,7 @@ static std::vector process_binary_operator(V v, if (v->fun_ref) { // almost all operators, fun_ref was assigned at type inferring std::vector args_vars = pre_compile_tensor(code, {v->get_lhs(), v->get_rhs()}); - return gen_op_call(code, v->inferred_type, v->loc, std::move(args_vars), v->fun_ref); + return gen_op_call(code, v->inferred_type, v->loc, std::move(args_vars), v->fun_ref, "(binary-op)"); } if (t == tok_logical_and || t == tok_logical_or) { // do the following transformations: @@ -249,7 +453,7 @@ static std::vector process_binary_operator(V v, v_b_ne_0->mutate()->assign_fun_ref(lookup_global_symbol("_!=_")->as()); std::vector cond = pre_compile_expr(v->get_lhs(), code); tolk_assert(cond.size() == 1); - std::vector rvect = code.create_tmp_var(v->inferred_type, v->loc); + std::vector rvect = code.create_tmp_var(v->inferred_type, v->loc, "(cond)"); Op& if_op = code.emplace_back(v->loc, Op::_If, cond); code.push_set_cur(if_op.block0); code.emplace_back(v->loc, Op::_Let, rvect, pre_compile_expr(t == tok_logical_and ? v_b_ne_0 : v_1, code)); @@ -265,13 +469,13 @@ static std::vector process_binary_operator(V v, static std::vector process_unary_operator(V v, CodeBlob& code) { std::vector args_vars = pre_compile_tensor(code, {v->get_rhs()}); - return gen_op_call(code, v->inferred_type, v->loc, std::move(args_vars), v->fun_ref); + return gen_op_call(code, v->inferred_type, v->loc, std::move(args_vars), v->fun_ref, "(unary-op)"); } static std::vector process_ternary_operator(V v, CodeBlob& code) { std::vector cond = pre_compile_expr(v->get_cond(), code); tolk_assert(cond.size() == 1); - std::vector rvect = code.create_tmp_var(v->inferred_type, v->loc); + std::vector rvect = code.create_tmp_var(v->inferred_type, v->loc, "(cond)"); Op& if_op = code.emplace_back(v->loc, Op::_If, cond); code.push_set_cur(if_op.block0); code.emplace_back(v->get_when_true()->loc, Op::_Let, rvect, pre_compile_expr(v->get_when_true(), code)); @@ -282,13 +486,67 @@ static std::vector process_ternary_operator(V v return rvect; } -static std::vector process_dot_access(V v, CodeBlob& code, LValGlobs* lval_globs) { +static std::vector process_dot_access(V v, CodeBlob& code, LValContext* lval_ctx) { // it's NOT a method call `t.tupleSize()` (since such cases are handled by process_function_call) // it's `t.0`, `getUser().id`, and `t.tupleSize` (as a reference, not as a call) - // currently, nothing except a global function can be a target of dot access - const FunctionData* fun_ref = v->target; + if (!v->is_target_fun_ref()) { + TypePtr obj_type = v->get_obj()->inferred_type; + int index_at = std::get(v->target); + // `tensorVar.0`; since a tensor of N elems are N vars on a stack actually, calculate offset + if (const auto* t_tensor = obj_type->try_as()) { + if (lval_ctx) lval_ctx->register_lval(v->loc, v); + if (lval_ctx) lval_ctx->enter_rval_inside_lval(); + std::vector lhs_vars = pre_compile_expr(v->get_obj(), code, lval_ctx); + if (lval_ctx) lval_ctx->exit_rval_inside_lval(); + int stack_width = t_tensor->items[index_at]->calc_width_on_stack(); + int stack_offset = 0; + for (int i = 0; i < index_at; ++i) { + stack_offset += t_tensor->items[i]->calc_width_on_stack(); + } + return {lhs_vars.begin() + stack_offset, lhs_vars.begin() + stack_offset + stack_width}; + } + // `tupleVar.0`; not to mess up, separate rvalue and lvalue cases + if (obj_type->try_as() || obj_type->try_as()) { + if (!lval_ctx) { + // `tupleVar.0` as rvalue: the same as "tupleAt(tupleVar, 0)" written in terms of IR vars + std::vector tuple_ir_idx = pre_compile_expr(v->get_obj(), code); + std::vector index_ir_idx = code.create_tmp_var(TypeDataInt::create(), v->get_identifier()->loc, "(tuple-idx)"); + code.emplace_back(v->loc, Op::_IntConst, index_ir_idx, td::make_refint(index_at)); + std::vector field_ir_idx = code.create_tmp_var(v->inferred_type, v->loc, "(tuple-field)"); + tolk_assert(tuple_ir_idx.size() == 1 && field_ir_idx.size() == 1); // tuples contain only 1-slot values + const FunctionData* builtin_sym = lookup_global_symbol("tupleAt")->as(); + code.emplace_back(v->loc, Op::_Call, field_ir_idx, std::vector{tuple_ir_idx[0], index_ir_idx[0]}, builtin_sym); + return field_ir_idx; + } else { + // `tupleVar.0 = rhs`: finally "tupleSetAt(tupleVar, rhs, 0)" will be done + uint64_t hash = lval_ctx->register_lval(v->loc, v); + if (const var_idx_t* field_ir_idx = lval_ctx->exists_already_known_tuple_index(hash)) { + return {*field_ir_idx}; // `(t.0.0, t.0.1) = rhs`, then "t.0" will be read (tupleAt) once + } + lval_ctx->enter_rval_inside_lval(); + std::vector tuple_ir_idx = pre_compile_expr(v->get_obj(), code, lval_ctx); + lval_ctx->exit_rval_inside_lval(); + std::vector index_ir_idx = code.create_tmp_var(TypeDataInt::create(), v->get_identifier()->loc, "(tuple-idx)"); + code.emplace_back(v->loc, Op::_IntConst, index_ir_idx, td::make_refint(index_at)); + std::vector field_ir_idx = code.create_tmp_var(v->inferred_type, v->loc, "(tuple-field)"); + if (lval_ctx->is_rval_inside_lval()) { // for `t.0.1 = rhs` "t.0" is rvalue inside lvalue + // for `t.0 = rhs` don't call tupleAt, but for `t.0.1 = rhs` do for t.0 (still don't for t.0.1) + const FunctionData* builtin_sym = lookup_global_symbol("tupleAt")->as(); + code.emplace_back(v->loc, Op::_Call, field_ir_idx, std::vector{tuple_ir_idx[0], index_ir_idx[0]}, builtin_sym); + } + lval_ctx->register_modified_tuple_index(hash, tuple_ir_idx[0], index_ir_idx[0], field_ir_idx[0]); + vars_modification_watcher.trigger_callbacks(tuple_ir_idx, v->loc); + return field_ir_idx; + } + } + tolk_assert(false); + } + + // okay, v->target refs a function, like `obj.method`, filled at type inferring + // (currently, nothing except a global function can be referenced, no object-scope methods exist) + const FunctionData* fun_ref = std::get(v->target); tolk_assert(fun_ref); - return process_symbol(v->loc, fun_ref, code, lval_globs); + return pre_compile_symbol(v->loc, fun_ref, code, lval_ctx); } static std::vector process_function_call(V v, CodeBlob& code) { @@ -304,7 +562,7 @@ static std::vector process_function_call(V v, Code std::vector tfunc = pre_compile_expr(v->get_callee(), code); tolk_assert(tfunc.size() == 1); args_vars.push_back(tfunc[0]); - std::vector rvect = code.create_tmp_var(v->inferred_type, v->loc); + std::vector rvect = code.create_tmp_var(v->inferred_type, v->loc, "(call-ind)"); Op& op = code.emplace_back(v->loc, Op::_CallInd, rvect, std::move(args_vars)); op.set_impure_flag(); return rvect; @@ -349,28 +607,28 @@ static std::vector process_function_call(V v, Code for (const std::vector& list : vars_per_arg) { args_vars.insert(args_vars.end(), list.cbegin(), list.cend()); } - std::vector rvect_apply = gen_op_call(code, op_call_type, v->loc, std::move(args_vars), fun_ref); + std::vector rvect_apply = gen_op_call(code, op_call_type, v->loc, std::move(args_vars), fun_ref, "(fun-call)"); if (fun_ref->has_mutate_params()) { - LValGlobs local_globs; + LValContext local_lval; std::vector left; for (int i = 0; i < delta_self + v->get_num_args(); ++i) { if (fun_ref->parameters[i].is_mutate_parameter()) { AnyExprV arg_i = obj_leftmost && i == 0 ? obj_leftmost : args[i]; tolk_assert(arg_i->is_lvalue || i == 0); if (arg_i->is_lvalue) { - std::vector ith_var_idx = pre_compile_expr(arg_i, code, &local_globs); + std::vector ith_var_idx = pre_compile_expr(arg_i, code, &local_lval); left.insert(left.end(), ith_var_idx.begin(), ith_var_idx.end()); } else { left.insert(left.end(), vars_per_arg[0].begin(), vars_per_arg[0].end()); } } } - std::vector rvect = code.create_tmp_var(real_ret_type, v->loc); + std::vector rvect = code.create_tmp_var(real_ret_type, v->loc, "(fun-call)"); left.insert(left.end(), rvect.begin(), rvect.end()); - code.on_var_modification(left, v->loc); + vars_modification_watcher.trigger_callbacks(left, v->loc); code.emplace_back(v->loc, Op::_Let, std::move(left), rvect_apply); - local_globs.gen_ops_set_globs(code, v->loc); + local_lval.gen_ops_if_nonempty(code, v->loc); rvect_apply = rvect; } @@ -385,29 +643,29 @@ static std::vector process_function_call(V v, Code return rvect_apply; } -static std::vector process_tensor(V v, CodeBlob& code, LValGlobs* lval_globs) { - return pre_compile_tensor(code, v->get_items(), lval_globs); +static std::vector process_tensor(V v, CodeBlob& code, LValContext* lval_ctx) { + return pre_compile_tensor(code, v->get_items(), lval_ctx); } -static std::vector process_typed_tuple(V v, CodeBlob& code, LValGlobs* lval_globs) { - if (lval_globs) { // todo some time, make "var (a, [b,c]) = (1, [2,3])" work +static std::vector process_typed_tuple(V v, CodeBlob& code, LValContext* lval_ctx) { + if (lval_ctx) { // todo some time, make "var (a, [b,c]) = (1, [2,3])" work v->error("[...] can not be used as lvalue here"); } - std::vector left = code.create_tmp_var(v->inferred_type, v->loc); - std::vector right = pre_compile_tensor(code, v->get_items()); + std::vector left = code.create_tmp_var(v->inferred_type, v->loc, "(pack-tuple)"); + std::vector right = pre_compile_tensor(code, v->get_items(), lval_ctx); code.emplace_back(v->loc, Op::_Tuple, left, std::move(right)); return left; } static std::vector process_int_const(V v, CodeBlob& code) { - std::vector rvect = code.create_tmp_var(v->inferred_type, v->loc); + std::vector rvect = code.create_tmp_var(v->inferred_type, v->loc, "(int-const)"); code.emplace_back(v->loc, Op::_IntConst, rvect, v->intval); return rvect; } static std::vector process_string_const(V v, CodeBlob& code) { ConstantValue value = eval_const_init_value(v); - std::vector rvect = code.create_tmp_var(v->inferred_type, v->loc); + std::vector rvect = code.create_tmp_var(v->inferred_type, v->loc, "(str-const)"); if (value.is_int()) { code.emplace_back(v->loc, Op::_IntConst, rvect, value.as_int()); } else { @@ -418,21 +676,21 @@ static std::vector process_string_const(V v, CodeBl static std::vector process_bool_const(V v, CodeBlob& code) { const FunctionData* builtin_sym = lookup_global_symbol(v->bool_val ? "__true" : "__false")->as(); - return gen_op_call(code, v->inferred_type, v->loc, {}, builtin_sym); + return gen_op_call(code, v->inferred_type, v->loc, {}, builtin_sym, "(bool-const)"); } static std::vector process_null_keyword(V v, CodeBlob& code) { const FunctionData* builtin_sym = lookup_global_symbol("__null")->as(); - return gen_op_call(code, v->inferred_type, v->loc, {}, builtin_sym); + return gen_op_call(code, v->inferred_type, v->loc, {}, builtin_sym, "(null-literal)"); } static std::vector process_local_var(V v, CodeBlob& code) { if (v->marked_as_redef) { - return process_symbol(v->loc, v->var_ref, code, nullptr); + return pre_compile_symbol(v->loc, v->var_ref, code, nullptr); } tolk_assert(v->var_ref->ir_idx.empty()); - v->var_ref->mutate()->assign_ir_idx(code.create_var(v->inferred_type, v->var_ref, v->loc)); + v->var_ref->mutate()->assign_ir_idx(code.create_var(v->inferred_type, v->loc, v->var_ref->name)); return v->var_ref->ir_idx; } @@ -444,13 +702,13 @@ static std::vector process_local_vars_declaration(V process_underscore(V v, CodeBlob& code) { // when _ is used as left side of assignment, like `(cs, _) = cs.loadAndReturn()` - return code.create_tmp_var(v->inferred_type, v->loc); + return code.create_tmp_var(v->inferred_type, v->loc, "(underscore)"); } -std::vector pre_compile_expr(AnyExprV v, CodeBlob& code, LValGlobs* lval_globs) { +std::vector pre_compile_expr(AnyExprV v, CodeBlob& code, LValContext* lval_ctx) { switch (v->type) { case ast_reference: - return process_symbol(v->loc, v->as()->sym, code, lval_globs); + return pre_compile_symbol(v->loc, v->as()->sym, code, lval_ctx); case ast_assign: return process_assign(v->as(), code); case ast_set_assign: @@ -462,17 +720,17 @@ std::vector pre_compile_expr(AnyExprV v, CodeBlob& code, LValGlobs* l case ast_ternary_operator: return process_ternary_operator(v->as(), code); case ast_cast_as_operator: - return pre_compile_expr(v->as()->get_expr(), code, lval_globs); + return pre_compile_expr(v->as()->get_expr(), code, lval_ctx); case ast_dot_access: - return process_dot_access(v->as(), code, lval_globs); + return process_dot_access(v->as(), code, lval_ctx); case ast_function_call: return process_function_call(v->as(), code); case ast_parenthesized_expression: - return pre_compile_expr(v->as()->get_expr(), code, lval_globs); + return pre_compile_expr(v->as()->get_expr(), code, lval_ctx); case ast_tensor: - return process_tensor(v->as(), code, lval_globs); + return process_tensor(v->as(), code, lval_ctx); case ast_typed_tuple: - return process_typed_tuple(v->as(), code, lval_globs); + return process_typed_tuple(v->as(), code, lval_ctx); case ast_int_const: return process_int_const(v->as(), code); case ast_string_const: @@ -515,14 +773,14 @@ static void process_assert_statement(V v, CodeBlob& code) const FunctionData* builtin_sym = lookup_global_symbol("__throw_if_unless")->as(); std::vector args_vars = pre_compile_tensor(code, args); - gen_op_call(code, TypeDataVoid::create(), v->loc, std::move(args_vars), builtin_sym); + gen_op_call(code, TypeDataVoid::create(), v->loc, std::move(args_vars), builtin_sym, "(throw-call)"); } static void process_catch_variable(AnyExprV v_catch_var, CodeBlob& code) { if (auto v_ref = v_catch_var->try_as(); v_ref && v_ref->sym) { // not underscore const LocalVarData* var_ref = v_ref->sym->as(); tolk_assert(var_ref->ir_idx.empty()); - var_ref->mutate()->assign_ir_idx(code.create_var(v_catch_var->inferred_type, var_ref, v_catch_var->loc)); + var_ref->mutate()->assign_ir_idx(code.create_var(v_catch_var->inferred_type, v_catch_var->loc, var_ref->name)); } } @@ -621,11 +879,11 @@ static void process_throw_statement(V v, CodeBlob& code) { if (v->has_thrown_arg()) { const FunctionData* builtin_sym = lookup_global_symbol("__throw_arg")->as(); std::vector args_vars = pre_compile_tensor(code, {v->get_thrown_arg(), v->get_thrown_code()}); - gen_op_call(code, TypeDataVoid::create(), v->loc, std::move(args_vars), builtin_sym); + gen_op_call(code, TypeDataVoid::create(), v->loc, std::move(args_vars), builtin_sym, "(throw-call)"); } else { const FunctionData* builtin_sym = lookup_global_symbol("__throw")->as(); std::vector args_vars = pre_compile_tensor(code, {v->get_thrown_code()}); - gen_op_call(code, TypeDataVoid::create(), v->loc, std::move(args_vars), builtin_sym); + gen_op_call(code, TypeDataVoid::create(), v->loc, std::move(args_vars), builtin_sym, "(throw-call)"); } } @@ -699,7 +957,7 @@ static void convert_function_body_to_CodeBlob(const FunctionData* fun_ref, Funct for (int i = 0; i < fun_ref->get_num_params(); ++i) { const LocalVarData& param_i = fun_ref->parameters[i]; - std::vector ir_idx = blob->create_var(param_i.declared_type, ¶m_i, param_i.loc); + std::vector ir_idx = blob->create_var(param_i.declared_type, param_i.loc, param_i.name); rvect_import.insert(rvect_import.end(), ir_idx.begin(), ir_idx.end()); param_i.mutate()->assign_ir_idx(std::move(ir_idx)); } @@ -716,6 +974,7 @@ static void convert_function_body_to_CodeBlob(const FunctionData* fun_ref, Funct blob->close_blk(v_body->loc_end); code_body->set_code(blob); + tolk_assert(vars_modification_watcher.empty()); } static void convert_asm_body_to_AsmOp(const FunctionData* fun_ref, FunctionBodyAsm* asm_body) { diff --git a/tolk/pipe-check-rvalue-lvalue.cpp b/tolk/pipe-check-rvalue-lvalue.cpp index 943dfb96c..a824cc5d0 100644 --- a/tolk/pipe-check-rvalue-lvalue.cpp +++ b/tolk/pipe-check-rvalue-lvalue.cpp @@ -123,8 +123,8 @@ class CheckRValueLvalueVisitor final : public ASTVisitorFunctionBody { void visit(V v) override { // a reference to a method used as rvalue, like `var v = t.tupleAt` - if (const FunctionData* fun_ref = v->target; v->is_rvalue) { - validate_function_used_as_noncall(v, fun_ref); + if (v->is_rvalue && v->is_target_fun_ref()) { + validate_function_used_as_noncall(v, std::get(v->target)); } } diff --git a/tolk/pipe-infer-types-and-calls.cpp b/tolk/pipe-infer-types-and-calls.cpp index 011c83d75..ba5f77a78 100644 --- a/tolk/pipe-infer-types-and-calls.cpp +++ b/tolk/pipe-infer-types-and-calls.cpp @@ -124,6 +124,19 @@ static void fire_error_cannot_apply_operator(SrcLocation loc, std::string_view o throw ParseError(loc, "can not apply operator `" + op + "` to " + to_string(lhs->inferred_type) + " and " + to_string(rhs->inferred_type)); } +// fire an error on `untypedTupleVar.0` when used without a hint +GNU_ATTRIBUTE_NORETURN GNU_ATTRIBUTE_COLD +static void fire_error_cannot_deduce_untyped_tuple_access(SrcLocation loc, int index) { + std::string idx_access = "." + std::to_string(index); + throw ParseError(loc, "can not deduce type of `" + idx_access + "`; either assign it to variable like `var c: int = " + idx_access + "` or cast the result like `" + idx_access + " as int`"); +} + +// fire an error on `untypedTupleVar.0` when inferred as (int,int), or `[int, (int,int)]`, or other non-1 width in a tuple +GNU_ATTRIBUTE_NORETURN GNU_ATTRIBUTE_COLD +static void fire_error_cannot_put_non1_stack_width_arg_to_tuple(SrcLocation loc, TypePtr inferred_type) { + throw ParseError(loc, "can not put " + to_string(inferred_type) + " into a tuple, because it occupies " + std::to_string(inferred_type->calc_width_on_stack()) + " stack slots in TVM, not 1"); +} + // check correctness of called arguments counts and their type matching static void check_function_arguments(const FunctionData* fun_ref, V v, AnyExprV lhs_of_dot_call) { int delta_self = lhs_of_dot_call ? 1 : 0; @@ -466,6 +479,22 @@ class InferCheckTypesAndCallsAndFieldsVisitor final { return TypeDataTypedTuple::create(std::move(sub_hints)); } + // `a.0 = rhs` / `b.1.0 = rhs` (remember, its target is not assigned yet) + if (auto lhs_dot = lhs->try_as()) { + TypePtr obj_hint = calc_hint_from_assignment_lhs(lhs_dot->get_obj()); + std::string_view field_name = lhs_dot->get_field_name(); + if (field_name[0] >= '0' && field_name[0] <= '9') { + int index_at = std::stoi(std::string(field_name)); + if (const auto* t_tensor = obj_hint->try_as(); t_tensor && index_at < t_tensor->size()) { + return t_tensor->items[index_at]; + } + if (const auto* t_tuple = obj_hint->try_as(); t_tuple && index_at < t_tuple->size()) { + return t_tuple->items[index_at]; + } + } + return TypeDataUnknown::create(); + } + return TypeDataUnknown::create(); } @@ -562,8 +591,8 @@ class InferCheckTypesAndCallsAndFieldsVisitor final { return; } - // here is something strange and unhandled, like `f() = rhs` - // it will fail on later compilation steps (like rvalue/lvalue checks), but type inferring should pass + // here is something unhandled like `a.0 = rhs`, run regular inferring on rhs + // for something strange like `f() = rhs` type inferring will pass, but will fail later infer_any_expr(lhs, rhs_type); if (!lhs->inferred_type->can_rhs_be_assigned(rhs_type)) { err_loc->error("can not assign " + to_string(rhs_type) + " to " + to_string(lhs)); @@ -839,25 +868,56 @@ class InferCheckTypesAndCallsAndFieldsVisitor final { // it's NOT a method call `t.tupleSize()` (since such cases are handled by infer_function_call) // it's `t.0`, `getUser().id`, and `t.tupleSize` (as a reference, not as a call) infer_any_expr(v->get_obj()); + TypePtr obj_type = v->get_obj()->inferred_type; // our goal is to fill v->target knowing type of obj V v_ident = v->get_identifier(); // field/method name vertex V v_instantiationTs = v->get_instantiationTs(); std::string_view field_name = v_ident->name; - // for now, Tolk doesn't have structures, properties, and object-scoped methods - // so, only `t.tupleSize` is allowed, look up a global function - const Symbol* sym = lookup_global_symbol(field_name); - if (!sym) { - v_ident->error("undefined symbol `" + static_cast(field_name) + "`"); + // it can be indexed access (`tensorVar.0`, `tupleVar.1`) or a method (`t.tupleSize`) + // at first, check for indexed access + if (field_name[0] >= '0' && field_name[0] <= '9') { + int index_at = std::stoi(std::string(field_name)); + if (const auto* t_tensor = obj_type->try_as()) { + if (index_at >= t_tensor->size()) { + v_ident->error("invalid tensor index, expected 0.." + std::to_string(t_tensor->items.size() - 1)); + } + v->mutate()->assign_target(index_at); + assign_inferred_type(v, t_tensor->items[index_at]); + return; + } + if (const auto* t_tuple = obj_type->try_as()) { + if (index_at >= t_tuple->size()) { + v_ident->error("invalid tuple index, expected 0.." + std::to_string(t_tuple->items.size() - 1)); + } + v->mutate()->assign_target(index_at); + assign_inferred_type(v, t_tuple->items[index_at]); + return; + } + if (obj_type->try_as()) { + if (hint == nullptr) { + fire_error_cannot_deduce_untyped_tuple_access(v->loc, index_at); + } + if (hint->calc_width_on_stack() != 1) { + fire_error_cannot_put_non1_stack_width_arg_to_tuple(v->loc, hint); + } + v->mutate()->assign_target(index_at); + assign_inferred_type(v, hint); + return; + } + v_ident->error("type " + to_string(obj_type) + " is not indexable"); } - const FunctionData* fun_ref = sym->try_as(); + + // for now, Tolk doesn't have fields and object-scoped methods; `t.tupleSize` is a global function `tupleSize` + const Symbol* sym = lookup_global_symbol(field_name); + const FunctionData* fun_ref = sym ? sym->try_as() : nullptr; if (!fun_ref) { - v_ident->error("referencing a non-function"); + v_ident->error("non-existing field `" + static_cast(field_name) + "` of type " + to_string(obj_type)); } // `t.tupleSize` is ok, `cs.tupleSize` not - if (!fun_ref->parameters[0].declared_type->can_rhs_be_assigned(v->get_obj()->inferred_type)) { - v_ident->error("referencing a method for " + to_string(fun_ref->parameters[0]) + " with an object of type " + to_string(v->get_obj())); + if (!fun_ref->parameters[0].declared_type->can_rhs_be_assigned(obj_type)) { + v_ident->error("referencing a method for " + to_string(fun_ref->parameters[0]) + " with object of type " + to_string(obj_type)); } if (fun_ref->is_generic_function() && !v_instantiationTs) { @@ -896,21 +956,24 @@ class InferCheckTypesAndCallsAndFieldsVisitor final { } else if (auto v_dot = callee->try_as()) { // `obj.someMethod()` / `obj.someMethod()` / `getF().someMethod()` / `obj.SOME_CONST()` + // note, that dot_obj->target is not filled yet, since callee was not inferred yet delta_self = 1; dot_obj = v_dot->get_obj(); v_instantiationTs = v_dot->get_instantiationTs(); // present for `obj.someMethod()` infer_any_expr(dot_obj); - // for now, Tolk doesn't have object-scoped methods, so method resolving doesn't depend on obj type - // (in other words, `globalFunction(a)` = `a.globalFunction()`) - std::string_view method_name = v_dot->get_field_name(); - const Symbol* sym = lookup_global_symbol(method_name); - if (!sym) { - v_dot->get_identifier()->error("undefined symbol `" + static_cast(method_name) + "`"); - } - fun_ref = sym->try_as(); - if (!fun_ref) { - v_dot->get_identifier()->error("calling a non-function"); + // it can be indexed access (`tensorVar.0()`, `tupleVar.1()`) or a method (`t.tupleSize()`) + std::string_view field_name = v_dot->get_field_name(); + if (field_name[0] >= '0' && field_name[0] <= '9') { + // indexed access `ab.2()`, then treat `ab.2` just like an expression, fun_ref remains nullptr + // infer_dot_access() will be called for a callee, it will check type, index correctness, etc. + } else { + // for now, Tolk doesn't have fields and object-scoped methods; `t.tupleSize` is a global function `tupleSize` + const Symbol* sym = lookup_global_symbol(field_name); + fun_ref = sym ? sym->try_as() : nullptr; + if (!fun_ref) { + v_dot->get_identifier()->error("non-existing method `" + static_cast(field_name) + "` of type " + to_string(dot_obj)); + } } } else { @@ -926,7 +989,7 @@ class InferCheckTypesAndCallsAndFieldsVisitor final { assign_inferred_type(arg_i, arg_i->get_expr()); } - // handle `local_var()` / `getF()()` / `5()` / `SOME_CONST()` / `obj.method()()()` + // handle `local_var()` / `getF()()` / `5()` / `SOME_CONST()` / `obj.method()()()` / `tensorVar.0()` if (!fun_ref) { // treat callee like a usual expression, which must have "callable" inferred type infer_any_expr(callee); @@ -1017,6 +1080,9 @@ class InferCheckTypesAndCallsAndFieldsVisitor final { for (int i = 0; i < v->size(); ++i) { AnyExprV item = v->get_item(i); infer_any_expr(item, tuple_hint && i < tuple_hint->size() ? tuple_hint->items[i] : nullptr); + if (item->inferred_type->calc_width_on_stack() != 1) { + fire_error_cannot_put_non1_stack_width_arg_to_tuple(v->get_item(i)->loc, item->inferred_type); + } types_list.emplace_back(item->inferred_type); } assign_inferred_type(v, TypeDataTypedTuple::create(std::move(types_list))); diff --git a/tolk/tolk.h b/tolk/tolk.h index 7b44931ee..4086d7f78 100644 --- a/tolk/tolk.h +++ b/tolk/tolk.h @@ -44,21 +44,23 @@ typedef int var_idx_t; typedef int const_idx_t; struct TmpVar { - TypePtr v_type; - var_idx_t ir_idx; - const LocalVarData* v_sym; // points to var defined in code; nullptr for implicitly created tmp vars - SrcLocation where; - std::vector> on_modification; + var_idx_t ir_idx; // every var in IR represents 1 stack slot + TypePtr v_type; // calc_width_on_stack() is 1 + std::string name; // "x" for vars originated from user sources; "x.0" for tensor components; empty for implicitly created tmp vars + SrcLocation loc; // location of var declaration in sources or where a tmp var was originated +#ifdef TOLK_DEBUG + const char* desc = nullptr; // "origin" of tmp var, for debug output like `'15 (binary-op) '16 (glob-var)` +#endif - TmpVar(var_idx_t ir_idx, TypePtr type, const LocalVarData* v_sym, SrcLocation loc) - : v_type(type) - , ir_idx(ir_idx) - , v_sym(v_sym) - , where(loc) { + TmpVar(var_idx_t ir_idx, TypePtr v_type, std::string name, SrcLocation loc) + : ir_idx(ir_idx) + , v_type(v_type) + , name(std::move(name)) + , loc(loc) { } - void show(std::ostream& os, int omit_idx = 0) const; - void dump(std::ostream& os) const; + void show_as_stack_comment(std::ostream& os) const; + void show(std::ostream& os) const; }; struct VarDescr { @@ -602,7 +604,6 @@ struct AsmOpList { } 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()) { @@ -1018,13 +1019,10 @@ struct Stack { 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); - } + void show(); void opt_show() { if ((mode & (_StkCmt | _Shown)) == _StkCmt) { - show(mode); + show(); } } bool operator==(const Stack& y) const & { @@ -1108,9 +1106,15 @@ struct CodeBlob { #endif return res; } - std::vector create_var(TypePtr var_type, const LocalVarData* v_sym, SrcLocation loc); - std::vector create_tmp_var(TypePtr var_type, SrcLocation loc) { - return create_var(var_type, nullptr, loc); + std::vector create_var(TypePtr var_type, SrcLocation loc, std::string name); + std::vector create_tmp_var(TypePtr var_type, SrcLocation loc, const char* desc) { + std::vector ir_idx = create_var(var_type, loc, {}); +#ifdef TOLK_DEBUG + for (var_idx_t v : ir_idx) { + vars[v].desc = desc; + } +#endif + return ir_idx; } bool compute_used_code_vars(); bool compute_used_code_vars(std::unique_ptr& ops, const VarDescrList& var_info, bool edit) const; @@ -1135,14 +1139,6 @@ struct CodeBlob { void mark_noreturn(); void generate_code(AsmOpList& out_list, int mode = 0); void generate_code(std::ostream& os, int mode = 0, int indent = 0); - - void on_var_modification(const std::vector& left_lval_indices, SrcLocation here) const { - for (var_idx_t ir_idx : left_lval_indices) { - for (auto& f : vars.at(ir_idx).on_modification) { - f(here); - } - } - } }; // defined in builtins.cpp From 5b44e01455eed921150e38898ad79d2407bb4e76 Mon Sep 17 00:00:00 2001 From: tolk-vm Date: Mon, 27 Jan 2025 10:33:24 +0300 Subject: [PATCH 34/38] [Tolk] Allow `cell` and `slice` be valid identifiers They are not keywords anymore. > var cell = ...; > var cell: cell = ...; Motivation: in the future, when structures are implemented, this obviously should be valid: > struct a { ... } > var a = ...; Struct fields will also be allowed to have names int/slice/cell. --- tolk-tester/tests/assignment-tests.tolk | 12 +++++ tolk-tester/tests/invalid-catch-1.tolk | 6 +-- tolk-tester/tests/invalid-declaration-2.tolk | 2 +- tolk-tester/tests/invalid-declaration-4.tolk | 2 +- tolk-tester/tests/method_id.tolk | 2 + tolk/ast-from-tokens.cpp | 18 +------ tolk/lexer.cpp | 10 ---- tolk/lexer.h | 8 --- tolk/type-system.cpp | 56 ++++++++++---------- 9 files changed, 47 insertions(+), 69 deletions(-) diff --git a/tolk-tester/tests/assignment-tests.tolk b/tolk-tester/tests/assignment-tests.tolk index 89de8cf44..40761939c 100644 --- a/tolk-tester/tests/assignment-tests.tolk +++ b/tolk-tester/tests/assignment-tests.tolk @@ -14,6 +14,18 @@ fun autoInferIntNull(x: int) { return x; } +fun typesAsIdentifiers(builder: builder) { + var int = 1; + var cell = builder.endCell(); + var slice = cell.beginParse(); + { + var cell: cell = cell; + var tuple: tuple = createEmptyTuple(); + var bool: bool = tuple.tupleAt(0) > 0; + } + return int; +} + fun main(value: int) { var (x: int, y) = (autoInferIntNull(value), autoInferIntNull(value * 2)); if (x == null && y == null) { return null; } diff --git a/tolk-tester/tests/invalid-catch-1.tolk b/tolk-tester/tests/invalid-catch-1.tolk index 756722bb8..54f6e182b 100644 --- a/tolk-tester/tests/invalid-catch-1.tolk +++ b/tolk-tester/tests/invalid-catch-1.tolk @@ -1,12 +1,12 @@ fun main() { try { - } catch(int, arg) {} + } catch(if, arg) {} return 0; } /** @compilation_should_fail -@stderr expected identifier, got `int` -@stderr catch(int +@stderr expected identifier, got `if` +@stderr catch(if */ diff --git a/tolk-tester/tests/invalid-declaration-2.tolk b/tolk-tester/tests/invalid-declaration-2.tolk index 700632517..07ffb6838 100644 --- a/tolk-tester/tests/invalid-declaration-2.tolk +++ b/tolk-tester/tests/invalid-declaration-2.tolk @@ -4,5 +4,5 @@ fun main(int): int { /** @compilation_should_fail -@stderr expected parameter name, got `int` +@stderr expected `: `, got `)` */ diff --git a/tolk-tester/tests/invalid-declaration-4.tolk b/tolk-tester/tests/invalid-declaration-4.tolk index 183dda96f..62fd6c568 100644 --- a/tolk-tester/tests/invalid-declaration-4.tolk +++ b/tolk-tester/tests/invalid-declaration-4.tolk @@ -4,5 +4,5 @@ fun main() { /** @compilation_should_fail -@stderr probably, you use FunC-like declarations; valid syntax is `var x: int = ...` +@stderr expected `;`, got `x` */ diff --git a/tolk-tester/tests/method_id.tolk b/tolk-tester/tests/method_id.tolk index c2d0b9aad..e7e70d245 100644 --- a/tolk-tester/tests/method_id.tolk +++ b/tolk-tester/tests/method_id.tolk @@ -4,6 +4,8 @@ fun foo1(): int { return 111; } fun foo2(): int { return 222; } @method_id(10) fun foo3(): int { return 333; } +@method_id(11) +fun slice(slice: slice): slice { return slice; } fun main(): int { return 999; } /** diff --git a/tolk/ast-from-tokens.cpp b/tolk/ast-from-tokens.cpp index 3eb4385aa..f5855bc1f 100644 --- a/tolk/ast-from-tokens.cpp +++ b/tolk/ast-from-tokens.cpp @@ -111,16 +111,6 @@ static void diagnose_addition_in_bitshift(SrcLocation loc, std::string_view bits } } -// fire an error for FunC-style variable declaration, like "int i" -GNU_ATTRIBUTE_NORETURN GNU_ATTRIBUTE_COLD -static void fire_error_FunC_style_var_declaration(Lexer& lex) { - SrcLocation loc = lex.cur_location(); - std::string type_str = static_cast(lex.cur_str()); // int / slice / etc. - lex.next(); - std::string var_name = lex.tok() == tok_identifier ? static_cast(lex.cur_str()) : "name"; - throw ParseError(loc, "can't parse; probably, you use FunC-like declarations; valid syntax is `var " + var_name + ": " + type_str + " = ...`"); -} - // replace (a == null) and similar to isNull(a) (call of a built-in function) static AnyExprV maybe_replace_eq_null_with_isNull_call(V v) { bool has_null = v->get_lhs()->type == ast_null_keyword || v->get_rhs()->type == ast_null_keyword; @@ -377,14 +367,8 @@ static AnyExprV parse_expr100(Lexer& lex) { } return createV(loc, v_ident, v_instantiationTs); } - default: { - // show a proper error for `int i` (FunC-style declarations) - TokenType t = lex.tok(); - if (t == tok_int || t == tok_cell || t == tok_slice || t == tok_builder || t == tok_tuple) { - fire_error_FunC_style_var_declaration(lex); - } + default: lex.unexpected(""); - } } } diff --git a/tolk/lexer.cpp b/tolk/lexer.cpp index 78ec991e1..06913a5fc 100644 --- a/tolk/lexer.cpp +++ b/tolk/lexer.cpp @@ -331,7 +331,6 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase { if (str == "as") return tok_as; break; case 3: - if (str == "int") return tok_int; if (str == "var") return tok_var; if (str == "fun") return tok_fun; if (str == "asm") return tok_asm; @@ -342,18 +341,13 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase { case 4: if (str == "else") return tok_else; if (str == "true") return tok_true; - if (str == "cell") return tok_cell; if (str == "null") return tok_null; - if (str == "void") return tok_void; - if (str == "bool") return tok_bool; if (str == "self") return tok_self; if (str == "tolk") return tok_tolk; if (str == "type") return tok_type; if (str == "enum") return tok_enum; break; case 5: - if (str == "slice") return tok_slice; - if (str == "tuple") return tok_tuple; if (str == "const") return tok_const; if (str == "false") return tok_false; if (str == "redef") return tok_redef; @@ -374,16 +368,12 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase { if (str == "export") return tok_export; break; case 7: - if (str == "builder") return tok_builder; if (str == "builtin") return tok_builtin; break; case 8: if (str == "continue") return tok_continue; if (str == "operator") return tok_operator; break; - case 12: - if (str == "continuation") return tok_continuation; - break; default: break; } diff --git a/tolk/lexer.h b/tolk/lexer.h index 9dbfe3b61..58bc3640a 100644 --- a/tolk/lexer.h +++ b/tolk/lexer.h @@ -118,14 +118,6 @@ enum TokenType { tok_if, tok_else, - tok_int, - tok_cell, - tok_bool, - tok_slice, - tok_builder, - tok_continuation, - tok_tuple, - tok_void, tok_arrow, tok_as, diff --git a/tolk/type-system.cpp b/tolk/type-system.cpp index 401c72af5..c7122e100 100644 --- a/tolk/type-system.cpp +++ b/tolk/type-system.cpp @@ -581,40 +581,38 @@ std::vector parse_nested_type_list_in_parenthesis(Lexer& lex) { static TypePtr parse_simple_type(Lexer& lex) { switch (lex.tok()) { - case tok_int: - lex.next(); - return TypeDataInt::create(); - case tok_bool: - lex.next(); - return TypeDataBool::create(); - case tok_cell: - lex.next(); - return TypeDataCell::create(); - case tok_builder: - lex.next(); - return TypeDataBuilder::create(); - case tok_slice: - lex.next(); - return TypeDataSlice::create(); - case tok_tuple: - lex.next(); - return TypeDataTuple::create(); - case tok_continuation: - lex.next(); - return TypeDataContinuation::create(); - case tok_null: - lex.next(); - return TypeDataNullLiteral::create(); - case tok_void: - lex.next(); - return TypeDataVoid::create(); case tok_self: case tok_identifier: { SrcLocation loc = lex.cur_location(); - std::string text = static_cast(lex.cur_str()); + std::string_view str = lex.cur_str(); lex.next(); - return TypeDataUnresolved::create(std::move(text), loc); + switch (str.size()) { + case 3: + if (str == "int") return TypeDataInt::create(); + break; + case 4: + if (str == "cell") return TypeDataCell::create(); + if (str == "void") return TypeDataVoid::create(); + if (str == "bool") return TypeDataBool::create(); + break; + case 5: + if (str == "slice") return TypeDataSlice::create(); + if (str == "tuple") return TypeDataTuple::create(); + break; + case 7: + if (str == "builder") return TypeDataBuilder::create(); + break; + case 12: + if (str == "continuation") return TypeDataContinuation::create(); + break; + default: + break; + } + return TypeDataUnresolved::create(std::string(str), loc); } + case tok_null: + lex.next(); + return TypeDataNullLiteral::create(); case tok_oppar: { std::vector items = parse_nested_type_list_in_parenthesis(lex); if (items.size() == 1) { From e9d8f1611b3370257ed50de80627f88cdb4c8ff9 Mon Sep 17 00:00:00 2001 From: tolk-vm Date: Mon, 27 Jan 2025 10:34:23 +0300 Subject: [PATCH 35/38] [Tolk] Bump version to v0.8 --- crypto/smartcont/tolk-stdlib/common.tolk | 2 +- crypto/smartcont/tolk-stdlib/gas-payments.tolk | 2 +- crypto/smartcont/tolk-stdlib/lisp-lists.tolk | 2 +- crypto/smartcont/tolk-stdlib/tvm-dicts.tolk | 2 +- crypto/smartcont/tolk-stdlib/tvm-lowlevel.tolk | 2 +- tolk/tolk-version.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crypto/smartcont/tolk-stdlib/common.tolk b/crypto/smartcont/tolk-stdlib/common.tolk index 4c0c40075..5311ec2f7 100644 --- a/crypto/smartcont/tolk-stdlib/common.tolk +++ b/crypto/smartcont/tolk-stdlib/common.tolk @@ -1,7 +1,7 @@ // Standard library for Tolk (LGPL licence). // It contains common functions that are available out of the box, the user doesn't have to import anything. // More specific functions are required to be imported explicitly, like "@stdlib/tvm-dicts". -tolk 0.7 +tolk 0.8 /** Tuple manipulation primitives. diff --git a/crypto/smartcont/tolk-stdlib/gas-payments.tolk b/crypto/smartcont/tolk-stdlib/gas-payments.tolk index 83893354d..2ac32f489 100644 --- a/crypto/smartcont/tolk-stdlib/gas-payments.tolk +++ b/crypto/smartcont/tolk-stdlib/gas-payments.tolk @@ -1,5 +1,5 @@ // A part of standard library for Tolk -tolk 0.7 +tolk 0.8 /** Gas and payment related primitives. diff --git a/crypto/smartcont/tolk-stdlib/lisp-lists.tolk b/crypto/smartcont/tolk-stdlib/lisp-lists.tolk index 429f0cbfd..0cb178417 100644 --- a/crypto/smartcont/tolk-stdlib/lisp-lists.tolk +++ b/crypto/smartcont/tolk-stdlib/lisp-lists.tolk @@ -1,5 +1,5 @@ // A part of standard library for Tolk -tolk 0.7 +tolk 0.8 /** Lisp-style lists are nested 2-elements tuples: `(1, (2, (3, null)))` represents list `[1, 2, 3]`. diff --git a/crypto/smartcont/tolk-stdlib/tvm-dicts.tolk b/crypto/smartcont/tolk-stdlib/tvm-dicts.tolk index a47fe5426..5c4362392 100644 --- a/crypto/smartcont/tolk-stdlib/tvm-dicts.tolk +++ b/crypto/smartcont/tolk-stdlib/tvm-dicts.tolk @@ -1,5 +1,5 @@ // A part of standard library for Tolk -tolk 0.7 +tolk 0.8 /** Dictionaries are represented as `cell` data type (cells can store anything, dicts in particular). diff --git a/crypto/smartcont/tolk-stdlib/tvm-lowlevel.tolk b/crypto/smartcont/tolk-stdlib/tvm-lowlevel.tolk index ef7c2afe9..72a54aac2 100644 --- a/crypto/smartcont/tolk-stdlib/tvm-lowlevel.tolk +++ b/crypto/smartcont/tolk-stdlib/tvm-lowlevel.tolk @@ -1,5 +1,5 @@ // A part of standard library for Tolk -tolk 0.7 +tolk 0.8 /// Usually `c3` has a continuation initialized by the whole code of the contract. It is used for function calls. /// The primitive returns the current value of `c3`. diff --git a/tolk/tolk-version.h b/tolk/tolk-version.h index 7eaf55a7a..843260129 100644 --- a/tolk/tolk-version.h +++ b/tolk/tolk-version.h @@ -18,6 +18,6 @@ namespace tolk { -constexpr const char* TOLK_VERSION = "0.7.0"; +constexpr const char* TOLK_VERSION = "0.8.0"; } // namespace tolk From b1c9466df40d6bbfbb4d977fabdf9628aec926b3 Mon Sep 17 00:00:00 2001 From: tolk-vm Date: Mon, 27 Jan 2025 17:09:21 +0300 Subject: [PATCH 36/38] Suppress clang warning "ATOMIC_FLAG_INIT marked deprecated" (#1502) In C++20, macro 'ATOMIC_FLAG_INIT' has been marked as deprecated. We need still to use it to be able to compile for C++17. For now, just suppress this warning. --- tdutils/td/utils/SpinLock.h | 3 +++ tdutils/td/utils/logging.cpp | 3 +++ tdutils/td/utils/logging.h | 3 +++ tdutils/td/utils/port/detail/PollableFd.h | 3 +++ 4 files changed, 12 insertions(+) diff --git a/tdutils/td/utils/SpinLock.h b/tdutils/td/utils/SpinLock.h index b5bb62dbf..f0856f0c0 100644 --- a/tdutils/td/utils/SpinLock.h +++ b/tdutils/td/utils/SpinLock.h @@ -63,7 +63,10 @@ class SpinLock { } private: +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-pragma" std::atomic_flag flag_ = ATOMIC_FLAG_INIT; +#pragma clang diagnostic pop void unlock() { flag_.clear(std::memory_order_release); } diff --git a/tdutils/td/utils/logging.cpp b/tdutils/td/utils/logging.cpp index 345615f12..03d32ee2c 100644 --- a/tdutils/td/utils/logging.cpp +++ b/tdutils/td/utils/logging.cpp @@ -176,7 +176,10 @@ void TsCerr::enterCritical() { void TsCerr::exitCritical() { lock_.clear(std::memory_order_release); } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-pragma" TsCerr::Lock TsCerr::lock_ = ATOMIC_FLAG_INIT; +#pragma clang diagnostic pop class DefaultLog : public LogInterface { public: diff --git a/tdutils/td/utils/logging.h b/tdutils/td/utils/logging.h index 5c9a0621f..dbf4c64b3 100644 --- a/tdutils/td/utils/logging.h +++ b/tdutils/td/utils/logging.h @@ -343,7 +343,10 @@ class TsLog : public LogInterface { private: LogInterface *log_ = nullptr; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-pragma" std::atomic_flag lock_ = ATOMIC_FLAG_INIT; +#pragma clang diagnostic pop void enter_critical() { while (lock_.test_and_set(std::memory_order_acquire)) { // spin diff --git a/tdutils/td/utils/port/detail/PollableFd.h b/tdutils/td/utils/port/detail/PollableFd.h index dceea4f38..6cb621e82 100644 --- a/tdutils/td/utils/port/detail/PollableFd.h +++ b/tdutils/td/utils/port/detail/PollableFd.h @@ -149,7 +149,10 @@ class PollableFdInfo : private ListNode { private: NativeFd fd_{}; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-pragma" std::atomic_flag lock_ = ATOMIC_FLAG_INIT; +#pragma clang diagnostic pop PollFlagsSet flags_; #if TD_PORT_WINDOWS SpinLock observer_lock_; From c7271d97ae1af53def8f9487dba44bb613662762 Mon Sep 17 00:00:00 2001 From: neodix42 Date: Mon, 3 Feb 2025 12:16:11 +0400 Subject: [PATCH 37/38] Add smartcont+lib folders to release (#1508) * add folders smartcont and lib only to release for having a small download link * allow usage of patter in file name * upgrade upload-release-action@v2 to v3 * Revert "upgrade upload-release-action@v2 to v3" This reverts commit 516126084a8bda7524c557197c357f0e95b05a55. * use gh cli for upload smartcont_lib * use gh cli for upload smartcont_lib * gh requires gh_token * clean up --- .github/workflows/create-release.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 652aaef6b..05a3db265 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -4,6 +4,9 @@ on: [workflow_dispatch] permissions: write-all +env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + jobs: create-release: runs-on: ubuntu-22.04 @@ -498,6 +501,14 @@ jobs: asset_name: ton-linux-x86_64.zip tag: ${{ steps.tag.outputs.TAG }} + - name: Upload generic smartcont+lib artifact + run: | + mkdir smartcont_lib + cd smartcont_lib + cp -r ../artifacts/ton-x86_64-linux/{smartcont,lib} . + zip -r smartcont_lib.zip . + gh release upload ${{ steps.tag.outputs.TAG }} smartcont_lib.zip + - name: Upload Linux x86-64 single artifact - fift uses: svenstaro/upload-release-action@v2 with: From 3c245c614600df950c5ba1f44be146b16199a45e Mon Sep 17 00:00:00 2001 From: EmelyanenkoK Date: Mon, 3 Feb 2025 11:16:44 +0300 Subject: [PATCH 38/38] Add forgotten highload-v2 to unlock (#1511) --- crypto/block/transaction.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index 63e9065b6..ba50c581d 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -1194,6 +1194,8 @@ static td::optional override_gas_limit(const ComputePhaseConfig& cfg .new_limit = 70'000'000, .from_version = 9, .until = 1740787200}; accounts[parse_addr("EQCkoRp4OE-SFUoMEnYfL3vF43T3AzNfW8jyTC4yzk8cJqMS")] = { .new_limit = 70'000'000, .from_version = 9, .until = 1740787200}; + accounts[parse_addr("UQBN5ICras79U8FYEm71ws34n-ZNIQ0LRNpckOUsIV3OebnC")] = { + .new_limit = 70'000'000, .from_version = 9, .until = 1740787200}; accounts[parse_addr("EQBDanbCeUqI4_v-xrnAN0_I2wRvEIaLg1Qg2ZN5c6Zl1KOh")] = { .new_limit = 225'000'000, .from_version = 9, .until = 1740787200}; return accounts;