diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4d59081b..cbb858e1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -323,6 +323,7 @@ if (LATEX_FOUND)
add_latex_document(doc/tvm.tex TARGET_NAME ton_vm_description)
add_latex_document(doc/tblkch.tex TARGET_NAME ton_blockchain_description)
add_latex_document(doc/fiftbase.tex TARGET_NAME fift_basic_description)
+ add_latex_document(doc/catchain.tex TARGET_NAME catchain_consensus_description)
endif()
#END internal
@@ -374,6 +375,8 @@ add_subdirectory(validator-engine)
add_subdirectory(validator-engine-console)
add_subdirectory(dht-server)
add_subdirectory(utils)
+add_subdirectory(http)
+add_subdirectory(rldp-http-proxy)
endif()
#END internal
@@ -473,6 +476,9 @@ target_link_libraries(test-ton-collator overlay tdutils tdactor adnl tl_api dht
#add_executable(test-ext-client test/test-ext-client.cpp)
#target_link_libraries(test-ext-client tdutils tdactor adnl tl_api tl-lite-utils)
+add_executable(test-http test/test-http.cpp)
+target_link_libraries(test-http PRIVATE tonhttp)
+
get_directory_property(HAS_PARENT PARENT_DIRECTORY)
if (HAS_PARENT)
set(ALL_TEST_SOURCE
diff --git a/GPLv2 b/GPLv2
index c1e0c158..69910f2e 100644
--- a/GPLv2
+++ b/GPLv2
@@ -1,4 +1,4 @@
-/*
+/*
This file is part of TON Blockchain source code.
TON Blockchain is free software; you can redistribute it and/or
@@ -14,13 +14,13 @@
You should have received a copy of the GNU General Public License
along with TON Blockchain. If not, see .
- In addition, as a special exception, the copyright holders give permission
- to link the code of portions of this program with the OpenSSL library.
- You must obey the GNU General Public License in all respects for all
- of the code used other than OpenSSL. If you modify file(s) with this
- exception, you may extend this exception to your version of the file(s),
- but you are not obligated to do so. If you do not wish to do so, delete this
- exception statement from your version. If you delete this exception statement
+ In addition, as a special exception, the copyright holders give permission
+ to link the code of portions of this program with the OpenSSL library.
+ You must obey the GNU General Public License in all respects for all
+ of the code used other than OpenSSL. If you modify file(s) with this
+ exception, you may extend this exception to your version of the file(s),
+ but you are not obligated to do so. If you do not wish to do so, delete this
+ exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2020 Telegram Systems LLP
diff --git a/adnl/adnl-address-list.cpp b/adnl/adnl-address-list.cpp
index 8b92d925..fc3d81eb 100644
--- a/adnl/adnl-address-list.cpp
+++ b/adnl/adnl-address-list.cpp
@@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#include "adnl-address-list.hpp"
#include "adnl-peer-table.h"
diff --git a/adnl/adnl-address-list.h b/adnl/adnl-address-list.h
index 686ced65..d9dada69 100644
--- a/adnl/adnl-address-list.h
+++ b/adnl/adnl-address-list.h
@@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
diff --git a/adnl/adnl-channel.cpp b/adnl/adnl-channel.cpp
index f4e09cb0..5c8229ca 100644
--- a/adnl/adnl-channel.cpp
+++ b/adnl/adnl-channel.cpp
@@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#include "adnl-channel.hpp"
#include "adnl-peer.h"
diff --git a/adnl/adnl-channel.h b/adnl/adnl-channel.h
index a8e2c448..94b5877d 100644
--- a/adnl/adnl-channel.h
+++ b/adnl/adnl-channel.h
@@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
diff --git a/adnl/adnl-channel.hpp b/adnl/adnl-channel.hpp
index d4356f93..0c30fbd5 100644
--- a/adnl/adnl-channel.hpp
+++ b/adnl/adnl-channel.hpp
@@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
diff --git a/adnl/adnl-local-id.cpp b/adnl/adnl-local-id.cpp
index cd9d384e..714464be 100644
--- a/adnl/adnl-local-id.cpp
+++ b/adnl/adnl-local-id.cpp
@@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#include "td/utils/crypto.h"
#include "td/utils/Random.h"
diff --git a/adnl/adnl-local-id.h b/adnl/adnl-local-id.h
index 03998f7a..678adabd 100644
--- a/adnl/adnl-local-id.h
+++ b/adnl/adnl-local-id.h
@@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
diff --git a/adnl/adnl-network-manager.cpp b/adnl/adnl-network-manager.cpp
index 02e73ce0..d6882d85 100644
--- a/adnl/adnl-network-manager.cpp
+++ b/adnl/adnl-network-manager.cpp
@@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#include "adnl-network-manager.hpp"
#include "adnl-peer-table.h"
diff --git a/adnl/adnl-packet.h b/adnl/adnl-packet.h
index 107ed7e5..363a74c2 100644
--- a/adnl/adnl-packet.h
+++ b/adnl/adnl-packet.h
@@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
diff --git a/adnl/adnl-peer-table.cpp b/adnl/adnl-peer-table.cpp
index b4880de2..555ff47b 100644
--- a/adnl/adnl-peer-table.cpp
+++ b/adnl/adnl-peer-table.cpp
@@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#include "adnl-peer-table.hpp"
#include "adnl-peer.h"
diff --git a/adnl/adnl-peer-table.h b/adnl/adnl-peer-table.h
index 42447a44..536ed60e 100644
--- a/adnl/adnl-peer-table.h
+++ b/adnl/adnl-peer-table.h
@@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
diff --git a/adnl/adnl-peer-table.hpp b/adnl/adnl-peer-table.hpp
index c5c6acd8..1ef4f971 100644
--- a/adnl/adnl-peer-table.hpp
+++ b/adnl/adnl-peer-table.hpp
@@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
diff --git a/adnl/adnl-peer.cpp b/adnl/adnl-peer.cpp
index 4ff8044a..5e3e9700 100644
--- a/adnl/adnl-peer.cpp
+++ b/adnl/adnl-peer.cpp
@@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#include "adnl-peer.h"
#include "adnl-peer.hpp"
diff --git a/blockchain-explorer/blockchain-explorer-http.hpp b/blockchain-explorer/blockchain-explorer-http.hpp
index 130a3a99..e21f1b7e 100644
--- a/blockchain-explorer/blockchain-explorer-http.hpp
+++ b/blockchain-explorer/blockchain-explorer-http.hpp
@@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
diff --git a/blockchain-explorer/blockchain-explorer-query.cpp b/blockchain-explorer/blockchain-explorer-query.cpp
index db731f14..4c8ed7b1 100644
--- a/blockchain-explorer/blockchain-explorer-query.cpp
+++ b/blockchain-explorer/blockchain-explorer-query.cpp
@@ -24,7 +24,7 @@
from all source files in the program, then also delete it here.
along with TON Blockchain. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#include "blockchain-explorer-query.hpp"
#include "blockchain-explorer-http.hpp"
@@ -47,7 +47,7 @@
#include "vm/boc.h"
#include "vm/cellops.h"
#include "vm/cells/MerkleProof.h"
-#include "vm/continuation.h"
+#include "vm/vm.h"
#include "vm/cp0.h"
namespace {
@@ -198,8 +198,9 @@ void HttpQueryCommon::abort_query(td::Status error) {
HttpAnswer A{"error", prefix_};
A.abort(std::move(error));
auto page = A.finish();
- promise_.set_value(
- MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY));
+ auto R = MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY);
+ MHD_add_response_header(R, "Content-Type", "text/html");
+ promise_.set_value(std::move(R));
}
stop();
}
@@ -291,8 +292,9 @@ void HttpQueryBlockView::finish_query() {
A << HttpAnswer::RawData{root};
return A.finish();
}();
- promise_.set_value(
- MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY));
+ auto R = MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY);
+ MHD_add_response_header(R, "Content-Type", "text/html");
+ promise_.set_value(std::move(R));
}
stop();
}
@@ -488,8 +490,9 @@ void HttpQueryBlockInfo::finish_query() {
return A.finish();
}();
- promise_.set_value(
- MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY));
+ auto R = MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY);
+ MHD_add_response_header(R, "Content-Type", "text/html");
+ promise_.set_value(std::move(R));
}
stop();
}
@@ -709,8 +712,9 @@ void HttpQueryBlockSearch::finish_query() {
return A.finish();
}();
- promise_.set_value(
- MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY));
+ auto R = MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY);
+ MHD_add_response_header(R, "Content-Type", "text/html");
+ promise_.set_value(std::move(R));
}
stop();
}
@@ -795,8 +799,9 @@ void HttpQueryViewAccount::finish_query() {
A << HttpAnswer::AccountCell{addr_, res_block_id_, root, Q_roots};
return A.finish();
}();
- promise_.set_value(
- MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY));
+ auto R = MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY);
+ MHD_add_response_header(R, "Content-Type", "text/html");
+ promise_.set_value(std::move(R));
}
stop();
}
@@ -893,8 +898,9 @@ void HttpQueryViewTransaction::finish_query() {
}
return A.finish();
}();
- promise_.set_value(
- MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY));
+ auto R = MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY);
+ MHD_add_response_header(R, "Content-Type", "text/html");
+ promise_.set_value(std::move(R));
}
stop();
}
@@ -974,8 +980,9 @@ void HttpQueryViewTransaction2::finish_query() {
A << HttpAnswer::TransactionCell{addr_, block_id_, list};
return A.finish();
}();
- promise_.set_value(
- MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY));
+ auto R = MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY);
+ MHD_add_response_header(R, "Content-Type", "text/html");
+ promise_.set_value(std::move(R));
}
stop();
}
@@ -1178,8 +1185,9 @@ void HttpQueryConfig::finish_query() {
}
return A.finish();
}();
- promise_.set_value(
- MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY));
+ auto R = MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY);
+ MHD_add_response_header(R, "Content-Type", "text/html");
+ promise_.set_value(std::move(R));
}
stop();
}
@@ -1209,8 +1217,9 @@ void HttpQuerySendForm::finish_query() {
<< "";
return A.finish();
}();
- promise_.set_value(
- MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY));
+ auto R = MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY);
+ MHD_add_response_header(R, "Content-Type", "text/html");
+ promise_.set_value(std::move(R));
}
stop();
}
@@ -1270,8 +1279,9 @@ void HttpQuerySend::finish_query() {
}
return A.finish();
}();
- promise_.set_value(
- MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY));
+ auto R = MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY);
+ MHD_add_response_header(R, "Content-Type", "text/html");
+ promise_.set_value(std::move(R));
}
stop();
}
@@ -1434,8 +1444,9 @@ void HttpQueryRunMethod::finish_query() {
return A.finish();
}();
- promise_.set_value(
- MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY));
+ auto R = MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY);
+ MHD_add_response_header(R, "Content-Type", "text/html");
+ promise_.set_value(std::move(R));
}
stop();
}
@@ -1511,8 +1522,9 @@ void HttpQueryStatus::finish_query() {
A << "";
return A.finish();
}();
- promise_.set_value(
- MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY));
+ auto R = MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY);
+ MHD_add_response_header(R, "Content-Type", "text/html");
+ promise_.set_value(std::move(R));
}
stop();
}
diff --git a/blockchain-explorer/blockchain-explorer.cpp b/blockchain-explorer/blockchain-explorer.cpp
index adaf4084..47ce7378 100644
--- a/blockchain-explorer/blockchain-explorer.cpp
+++ b/blockchain-explorer/blockchain-explorer.cpp
@@ -23,7 +23,7 @@
from all source files in the program, then also delete it here.
along with TON Blockchain. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#include "adnl/adnl-ext-client.h"
#include "adnl/utils.hpp"
@@ -52,7 +52,6 @@
#include "vm/boc.h"
#include "vm/cellops.h"
#include "vm/cells/MerkleProof.h"
-#include "vm/continuation.h"
#include "vm/cp0.h"
#include "auto/tl/lite_api.h"
diff --git a/blockchain-explorer/blockchain-explorer.hpp b/blockchain-explorer/blockchain-explorer.hpp
index 8e2ae0e6..b6bc15f1 100644
--- a/blockchain-explorer/blockchain-explorer.hpp
+++ b/blockchain-explorer/blockchain-explorer.hpp
@@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt
index ff7d9227..0897166f 100644
--- a/crypto/CMakeLists.txt
+++ b/crypto/CMakeLists.txt
@@ -36,6 +36,7 @@ set(TON_CRYPTO_SOURCE
vm/tonops.cpp
vm/boc.cpp
vm/utils.cpp
+ vm/vm.cpp
tl/tlblib.cpp
Ed25519.h
@@ -82,6 +83,7 @@ set(TON_CRYPTO_SOURCE
vm/tonops.h
vm/vmstate.h
vm/utils.h
+ vm/vm.h
vm/cells.h
vm/cellslice.h
@@ -204,6 +206,8 @@ set(BLOCK_SOURCE
set(SMC_ENVELOPE_SOURCE
smc-envelope/GenericAccount.cpp
smc-envelope/HighloadWallet.cpp
+ smc-envelope/HighloadWalletV2.cpp
+ smc-envelope/ManualDns.cpp
smc-envelope/MultisigWallet.cpp
smc-envelope/SmartContract.cpp
smc-envelope/SmartContractCode.cpp
@@ -214,12 +218,15 @@ set(SMC_ENVELOPE_SOURCE
smc-envelope/GenericAccount.h
smc-envelope/HighloadWallet.h
+ smc-envelope/HighloadWalletV2.h
+ smc-envelope/ManualDns.h
smc-envelope/MultisigWallet.h
smc-envelope/SmartContract.h
smc-envelope/SmartContractCode.h
smc-envelope/TestGiver.h
smc-envelope/TestWallet.h
smc-envelope/Wallet.h
+ smc-envelope/WalletInterface.h
smc-envelope/WalletV3.h
)
@@ -359,12 +366,14 @@ if (NOT CMAKE_CROSSCOMPILING)
GenFif(DEST smartcont/auto/wallet3-code SOURCE smartcont/wallet3-code.fc NAME wallet3)
GenFif(DEST smartcont/auto/simple-wallet-code SOURCE smartcont/simple-wallet-code.fc NAME simple-wallet)
GenFif(DEST smartcont/auto/highload-wallet-code SOURCE smartcont/highload-wallet-code.fc NAME highload-wallet)
- GenFif(DEST smartcont/auto/highload-wallet-v2-code SOURCE smartcont/highload-wallet-v2-code.fc NAME highoad-wallet-v2)
+ GenFif(DEST smartcont/auto/highload-wallet-v2-code SOURCE smartcont/highload-wallet-v2-code.fc NAME highload-wallet-v2)
GenFif(DEST smartcont/auto/elector-code SOURCE smartcont/elector-code.fc NAME elector-code)
GenFif(DEST smartcont/auto/multisig-code SOURCE smartcont/multisig-code.fc NAME multisig)
GenFif(DEST smartcont/auto/restricted-wallet-code SOURCE smartcont/restricted-wallet-code.fc NAME restricted-wallet)
GenFif(DEST smartcont/auto/restricted-wallet2-code SOURCE smartcont/restricted-wallet2-code.fc NAME restricted-wallet2)
+ GenFif(DEST smartcont/auto/dns-manual-code SOURCE smartcont/dns-manual-code.fc NAME dns-manual)
+
GenFif(DEST smartcont/auto/simple-wallet-ext-code SOURCE smartcont/simple-wallet-ext-code.fc NAME simple-wallet-ext)
endif()
diff --git a/crypto/block/mc-config.cpp b/crypto/block/mc-config.cpp
index 46186caa..719d76b0 100644
--- a/crypto/block/mc-config.cpp
+++ b/crypto/block/mc-config.cpp
@@ -567,6 +567,21 @@ td::Result Config::do_get_gas_limits_prices(td::Ref c
}
return res;
}
+
+td::Result Config::get_dns_root_addr() const {
+ auto cell = get_config_param(4);
+ if (cell.is_null()) {
+ return td::Status::Error(PSLICE() << "configuration parameter " << 4 << " with dns root address is absent");
+ }
+ auto cs = vm::load_cell_slice(std::move(cell));
+ if (cs.size() != 0x100) {
+ return td::Status::Error(PSLICE() << "configuration parameter " << 4 << " with dns root address has wrong size");
+ }
+ ton::StdSmcAddress res;
+ CHECK(cs.fetch_bits_to(res));
+ return res;
+}
+
td::Result Config::get_gas_limits_prices(bool is_masterchain) const {
auto id = is_masterchain ? 20 : 21;
auto cell = get_config_param(id);
diff --git a/crypto/block/mc-config.h b/crypto/block/mc-config.h
index 55bf1122..8541110f 100644
--- a/crypto/block/mc-config.h
+++ b/crypto/block/mc-config.h
@@ -14,7 +14,7 @@
You should have received a copy of the GNU General Public License
along with TON Blockchain. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include "common/refcnt.hpp"
@@ -534,6 +534,7 @@ class Config {
bool create_stats_enabled() const {
return has_capability(ton::capCreateStatsEnabled);
}
+ td::Result get_dns_root_addr() const;
bool set_block_id_ext(const ton::BlockIdExt& block_id_ext);
td::Result> get_special_smartcontracts(bool without_config = false) const;
bool is_special_smartcontract(const ton::StdSmcAddress& addr) const;
diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp
index df4cf883..2c5f4488 100644
--- a/crypto/block/transaction.cpp
+++ b/crypto/block/transaction.cpp
@@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#include "block/transaction.h"
#include "block/block.h"
@@ -23,7 +23,7 @@
#include "td/utils/bits.h"
#include "td/utils/uint128.h"
#include "ton/ton-shard.h"
-#include "vm/continuation.h"
+#include "vm/vm.h"
namespace block {
using td::Ref;
@@ -691,12 +691,16 @@ bool ComputePhaseConfig::parse_GasLimitsPrices(Ref cs, td::RefInt
}
block::gen::GasLimitsPrices::Record_gas_flat_pfx flat;
if (tlb::csr_unpack(cs, flat)) {
- bool ok = parse_GasLimitsPrices(std::move(flat.other), freeze_due_limit, delete_due_limit);
- flat_gas_limit = flat.flat_gas_limit;
- flat_gas_price = flat.flat_gas_price;
- return ok;
+ return parse_GasLimitsPrices_internal(std::move(flat.other), freeze_due_limit, delete_due_limit,
+ flat.flat_gas_limit, flat.flat_gas_price);
+ } else {
+ return parse_GasLimitsPrices_internal(std::move(cs), freeze_due_limit, delete_due_limit);
}
- flat_gas_limit = flat_gas_price = 0;
+}
+
+bool ComputePhaseConfig::parse_GasLimitsPrices_internal(Ref cs, td::RefInt256& freeze_due_limit,
+ td::RefInt256& delete_due_limit, td::uint64 _flat_gas_limit,
+ td::uint64 _flat_gas_price) {
auto f = [&](const auto& r, td::uint64 spec_limit) {
gas_limit = r.gas_limit;
special_gas_limit = spec_limit;
@@ -716,6 +720,8 @@ bool ComputePhaseConfig::parse_GasLimitsPrices(Ref cs, td::RefInt
return false;
}
}
+ flat_gas_limit = _flat_gas_limit;
+ flat_gas_price = _flat_gas_price;
compute_threshold();
return true;
}
@@ -1010,10 +1016,9 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
total_fees += cp.gas_fees;
balance -= cp.gas_fees;
}
- if (verbosity > 2) {
- std::cerr << "gas fees: " << cp.gas_fees << " = " << cfg.gas_price256 << " * " << cp.gas_used
- << " /2^16 ; price=" << cfg.gas_price << "; remaining balance=" << balance << std::endl;
- }
+ LOG(DEBUG) << "gas fees: " << cp.gas_fees->to_dec_string() << " = " << cfg.gas_price256->to_dec_string() << " * "
+ << cp.gas_used << " /2^16 ; price=" << cfg.gas_price << "; flat rate=[" << cfg.flat_gas_price << " for "
+ << cfg.flat_gas_limit << "]; remaining balance=" << balance.to_str();
CHECK(td::sgn(balance.grams) >= 0);
}
return true;
diff --git a/crypto/block/transaction.h b/crypto/block/transaction.h
index 10150f1c..8b3a424e 100644
--- a/crypto/block/transaction.h
+++ b/crypto/block/transaction.h
@@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include "common/refcnt.hpp"
@@ -130,6 +130,11 @@ struct ComputePhaseConfig {
}
bool parse_GasLimitsPrices(Ref cs, td::RefInt256& freeze_due_limit, td::RefInt256& delete_due_limit);
bool parse_GasLimitsPrices(Ref cell, td::RefInt256& freeze_due_limit, td::RefInt256& delete_due_limit);
+
+ private:
+ bool parse_GasLimitsPrices_internal(Ref cs, td::RefInt256& freeze_due_limit,
+ td::RefInt256& delete_due_limit, td::uint64 flat_gas_limit = 0,
+ td::uint64 flat_gas_price = 0);
};
struct ActionPhaseConfig {
diff --git a/crypto/fift/lib/Asm.fif b/crypto/fift/lib/Asm.fif
index 46fb9564..7c09e82d 100644
--- a/crypto/fift/lib/Asm.fif
+++ b/crypto/fift/lib/Asm.fif
@@ -1005,6 +1005,11 @@ x{F902} @Defop SHA256U
x{F910} @Defop CHKSIGNU
x{F911} @Defop CHKSIGNS
+x{F940} @Defop CDATASIZEQ
+x{F941} @Defop CDATASIZE
+x{F942} @Defop SDATASIZEQ
+x{F943} @Defop SDATASIZE
+
x{FA00} dup @Defop LDGRAMS @Defop LDVARUINT16
x{FA01} @Defop LDVARINT16
x{FA02} dup @Defop STGRAMS @Defop STVARUINT16
diff --git a/crypto/fift/lib/TonUtil.fif b/crypto/fift/lib/TonUtil.fif
index 71624798..796390f9 100644
--- a/crypto/fift/lib/TonUtil.fif
+++ b/crypto/fift/lib/TonUtil.fif
@@ -34,8 +34,10 @@ library TonUtil // TON Blockchain Fift Library
1 and 0=
} : parse-smc-addr
+// ( x -- ) Displays a 64-digit hex number
+{ 64 0x. } : 64x.
// ( wc addr -- ) Show address in : form
-{ swap ._ .":" 64 0x. } : .addr
+{ swap ._ .":" 64x. } : .addr
// ( wc addr flags -- ) Show address in base64url form
{ smca>$ type } : .Addr
// ( wc addr fname -- ) Save address to file in 36-byte format
diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp
index 2a36cf8f..a4067eb9 100644
--- a/crypto/fift/words.cpp
+++ b/crypto/fift/words.cpp
@@ -32,7 +32,7 @@
#include "vm/cells.h"
#include "vm/cellslice.h"
-#include "vm/continuation.h"
+#include "vm/vm.h"
#include "vm/cp0.h"
#include "vm/dict.h"
#include "vm/boc.h"
@@ -1534,7 +1534,7 @@ void interpret_dict_add(vm::Stack& stack, vm::Dictionary::SetMode mode, bool add
stack.push_bool(res);
}
-void interpret_dict_get(vm::Stack& stack, int sgnd) {
+void interpret_dict_get(vm::Stack& stack, int sgnd, int mode) {
int n = stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict{stack.pop_maybe_cell(), n};
unsigned char buffer[vm::Dictionary::max_key_bytes];
@@ -1543,12 +1543,16 @@ void interpret_dict_get(vm::Stack& stack, int sgnd) {
if (!key.is_valid()) {
throw IntError{"not enough bits for a dictionary key"};
}
- auto res = dict.lookup(std::move(key));
- if (res.not_null()) {
+ auto res = (mode & 4 ? dict.lookup_delete(std::move(key)) : dict.lookup(std::move(key)));
+ if (mode & 4) {
+ stack.push_maybe_cell(std::move(dict).extract_root_cell());
+ }
+ bool found = res.not_null();
+ if (found && (mode & 2)) {
stack.push_cellslice(std::move(res));
- stack.push_bool(true);
- } else {
- stack.push_bool(false);
+ }
+ if (mode & 1) {
+ stack.push_bool(found);
}
}
@@ -1572,15 +1576,15 @@ void interpret_dict_map(IntCtx& ctx) {
ctx.stack.push_maybe_cell(std::move(dict).extract_root_cell());
}
-void interpret_dict_map_ext(IntCtx& ctx) {
+void interpret_dict_map_ext(IntCtx& ctx, bool sgnd) {
auto func = pop_exec_token(ctx);
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict{ctx.stack.pop_maybe_cell(), n};
- vm::Dictionary::map_func_t map_func = [&ctx, func](vm::CellBuilder& cb, Ref cs_ref,
- td::ConstBitPtr key, int key_len) -> bool {
+ vm::Dictionary::map_func_t map_func = [&ctx, func, sgnd](vm::CellBuilder& cb, Ref cs_ref,
+ td::ConstBitPtr key, int key_len) -> bool {
ctx.stack.push_builder(Ref(cb));
td::RefInt256 x{true};
- x.unique_write().import_bits(key, key_len, false);
+ x.unique_write().import_bits(key, key_len, sgnd);
ctx.stack.push_int(std::move(x));
ctx.stack.push_cellslice(std::move(cs_ref));
func->run(ctx);
@@ -1596,20 +1600,20 @@ void interpret_dict_map_ext(IntCtx& ctx) {
ctx.stack.push_maybe_cell(std::move(dict).extract_root_cell());
}
-void interpret_dict_foreach(IntCtx& ctx) {
+void interpret_dict_foreach(IntCtx& ctx, bool sgnd) {
auto func = pop_exec_token(ctx);
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict{ctx.stack.pop_maybe_cell(), n};
- vm::Dictionary::foreach_func_t foreach_func = [&ctx, func](Ref cs_ref, td::ConstBitPtr key,
- int key_len) -> bool {
+ vm::Dictionary::foreach_func_t foreach_func = [&ctx, func, sgnd](Ref cs_ref, td::ConstBitPtr key,
+ int key_len) -> bool {
td::RefInt256 x{true};
- x.unique_write().import_bits(key, key_len, false);
+ x.unique_write().import_bits(key, key_len, sgnd);
ctx.stack.push_int(std::move(x));
ctx.stack.push_cellslice(std::move(cs_ref));
func->run(ctx);
return ctx.stack.pop_bool();
};
- ctx.stack.push_bool(dict.check_for_each(std::move(foreach_func)));
+ ctx.stack.push_bool(dict.check_for_each(std::move(foreach_func), sgnd));
}
void interpret_dict_merge(IntCtx& ctx) {
@@ -2752,23 +2756,31 @@ void init_words_common(Dictionary& d) {
d.def_stack_word("sdict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, false, -1));
d.def_stack_word("b>sdict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, true, -1));
d.def_stack_word("b>sdict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, true, -1));
- d.def_stack_word("sdict@ ", std::bind(interpret_dict_get, _1, -1));
+ d.def_stack_word("sdict@ ", std::bind(interpret_dict_get, _1, -1, 3));
+ d.def_stack_word("sdict@- ", std::bind(interpret_dict_get, _1, -1, 7));
+ d.def_stack_word("sdict- ", std::bind(interpret_dict_get, _1, -1, 5));
d.def_stack_word("udict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, false, 0));
d.def_stack_word("udict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, false, 0));
d.def_stack_word("b>udict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, true, 0));
d.def_stack_word("b>udict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, true, 0));
- d.def_stack_word("udict@ ", std::bind(interpret_dict_get, _1, 0));
+ d.def_stack_word("udict@ ", std::bind(interpret_dict_get, _1, 0, 3));
+ d.def_stack_word("udict@- ", std::bind(interpret_dict_get, _1, 0, 7));
+ d.def_stack_word("udict- ", std::bind(interpret_dict_get, _1, 0, 5));
d.def_stack_word("idict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, false, 1));
d.def_stack_word("idict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, false, 1));
d.def_stack_word("b>idict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, true, 1));
d.def_stack_word("b>idict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, true, 1));
- d.def_stack_word("idict@ ", std::bind(interpret_dict_get, _1, 1));
+ d.def_stack_word("idict@ ", std::bind(interpret_dict_get, _1, 1, 3));
+ d.def_stack_word("idict@- ", std::bind(interpret_dict_get, _1, 1, 7));
+ d.def_stack_word("idict- ", std::bind(interpret_dict_get, _1, 1, 5));
d.def_stack_word("pfxdict!+ ", std::bind(interpret_pfx_dict_add, _1, vm::Dictionary::SetMode::Add, false));
d.def_stack_word("pfxdict! ", std::bind(interpret_pfx_dict_add, _1, vm::Dictionary::SetMode::Set, false));
d.def_stack_word("pfxdict@ ", interpret_pfx_dict_get);
d.def_ctx_word("dictmap ", interpret_dict_map);
- d.def_ctx_word("dictmapext ", interpret_dict_map_ext);
- d.def_ctx_word("dictforeach ", interpret_dict_foreach);
+ d.def_ctx_word("dictmapext ", std::bind(interpret_dict_map_ext, _1, false));
+ d.def_ctx_word("idictmapext ", std::bind(interpret_dict_map_ext, _1, true));
+ d.def_ctx_word("dictforeach ", std::bind(interpret_dict_foreach, _1, false));
+ d.def_ctx_word("idictforeach ", std::bind(interpret_dict_foreach, _1, true));
d.def_ctx_word("dictmerge ", interpret_dict_merge);
d.def_ctx_word("dictdiff ", interpret_dict_diff);
// slice/bitstring constants
diff --git a/crypto/smartcont/dns-manual-code.fc b/crypto/smartcont/dns-manual-code.fc
new file mode 100644
index 00000000..7e34a1cc
--- /dev/null
+++ b/crypto/smartcont/dns-manual-code.fc
@@ -0,0 +1,381 @@
+{-
+ Originally created by:
+ /------------------------------------------------------------------------\
+ | Created for: Telegram (Open Network) Blockchain Contest |
+ | Task 3: DNS Resolver (Manually controlled) |
+ >------------------------------------------------------------------------<
+ | Author: Oleksandr Murzin (tg: @skydev / em: alexhacker64@gmail.com) |
+ | October 2019 |
+ \------------------------------------------------------------------------/
+-}
+
+;;===========================================================================;;
+;; Custom ASM instructions ;;
+;;===========================================================================;;
+
+;; Args: s D n | Success: s' x s'' -1 | Failure: s 0 -> s N N 0
+(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key)
+ asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
+
+;; Args: x k D n | Success: D' -1 | Failure: D 0
+(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value)
+ asm(value key dict key_len) "PFXDICTSET";
+
+;; Args: k D n | Success: D' -1 | Failure: D 0
+(cell, int) pfxdict_delete?(cell dict, int key_len, slice key)
+ asm(key dict key_len) "PFXDICTDEL";
+
+slice slice_last(slice s, int len) asm "SDCUTLAST";
+
+;; Actually, equivalent to dictionaries, provided for clarity
+(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
+builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
+
+(cell, ()) pfxdict_set_ref(cell dict, int key_len, slice key, cell value) {
+ throw_unless(33, dict~pfxdict_set?(key_len, key, begin_cell().store_maybe_ref(value).end_cell().begin_parse()));
+ return (dict, ());
+}
+
+(slice, cell, slice, int) pfxdict_get_ref(cell dict, int key_len, slice key) {
+ (slice pfx, slice val, slice tail, int succ) = dict.pfxdict_get?(key_len, key);
+ cell res = null();
+ if (succ) {
+ res = val~load_maybe_ref();
+ }
+ return (pfx, res, tail, succ);
+}
+
+;;===========================================================================;;
+;; Utility functions ;;
+;;===========================================================================;;
+
+(int, int, int, cell, cell) load_data() {
+ slice cs = get_data().begin_parse();
+ var res = (cs~load_uint(32), cs~load_uint(64), cs~load_uint(256), cs~load_dict(), cs~load_dict());
+ cs.end_parse();
+ return res;
+}
+
+() store_data(int subwallet, int last_cleaned, int public_key, cell root, old_queries) impure {
+ set_data(begin_cell()
+ .store_uint(subwallet, 32)
+ .store_uint(last_cleaned, 64)
+ .store_uint(public_key, 256)
+ .store_dict(root)
+ .store_dict(old_queries)
+ .end_cell());
+}
+
+;;===========================================================================;;
+;; Internal message handler (Code 0) ;;
+;;===========================================================================;;
+
+() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
+ ;; not interested at all
+}
+
+;;===========================================================================;;
+;; External message handler (Code -1) ;;
+;;===========================================================================;;
+
+{-
+ External message structure:
+ [Bytes<512b>:signature] [UInt<32b>:seqno] [UInt<6b>:operation]
+ [Either b0: inline name (<= 58-x Bytes) or b1: reference-stored name)
+ x depends on operation
+ Use of 6-bit op instead of 32-bit allows to save 4 bytes for inline name
+ Inline [Name] structure: [UInt<6b>:length] [Bytes:data]
+ Operations (continuation of message):
+ 00 Contract initialization message (only if seqno = 0) (x=-)
+ 11 VSet: set specified value to specified subdomain->category (x=2)
+ [Int<16b>:category] [Name>:subdomain] [Cell<1r>:value]
+ 12 VDel: delete specified subdomain->category (x=2)
+ [Int<16b>:category] [Name>:subdomain]
+ 21 DSet: replace entire category dictionary of domain with provided (x=0)
+ [Name>:subdomain] [Cell<1r>:new_cat_table]
+ 22 DDel: delete entire category dictionary of specified domain (x=0)
+ [Name>:subdomain]
+ 31 TSet: replace ENTIRE DOMAIN TABLE with the provided tree root cell (x=-)
+ [Cell<1r>:new_domains_table]
+ 32 TDel: nullify ENTIRE DOMAIN TABLE (x=-)
+ 51 OSet: replace owner public key with a new one (x=-)
+ [UInt<256b>:new_public_key]
+-}
+
+(cell, slice) process_op(cell root, slice ops) {
+ int op = ops~load_uint(6);
+ int is_name_ref = (ops~load_uint(1) == 1);
+
+ ;; lets assume at this point that special operations 00..09 are handled
+ throw_if(45, op < 10);
+ slice name = ops; ;; anything! better do not begin or it costs much gas
+ cell cat_table = null();
+ int cat = 0;
+ if (op < 20) {
+ ;; for operations with codes 10..19 category is required
+ cat = ops~load_int(16);
+ }
+ int zeros = 0;
+ if (op < 30) {
+ ;; for operations with codes 10..29 name is required
+ if (is_name_ref) {
+ ;; name is stored in separate referenced cell
+ name = ops~load_ref().begin_parse();
+ } else {
+ ;; name is stored inline
+ int name_len = ops~load_uint(6) * 8;
+ name = ops~load_bits(name_len);
+ }
+ ;; at least one character not counting \0
+ throw_unless(38, name.slice_bits() >= 16);
+ ;; name shall end with \0
+ (_, int name_last_byte) = name.slice_last(8).load_uint(8);
+ throw_unless(40, name_last_byte == 0);
+
+ ;; Multiple zero characters seem to be allowed as per github issue response
+ ;; Lets change the tactics!
+
+ int loop = -1;
+ slice cname = name;
+ ;; better safe then sorry, dont want to catch any of loop bugs
+ while (loop) {
+ int lval = cname~load_uint(8);
+ if (lval == 0) { zeros += 1; }
+ if (cname.slice_bits() == 0) { loop = 0; }
+ }
+ ;; throw_unless(39, zeros == 1);
+ }
+ ;; operation with codes 10..19 manipulate category dict
+ ;; lets try to find it and store into a variable
+ ;; operations with codes 20..29 replace / delete dict, no need
+ name = begin_cell().store_uint(zeros, 7).store_slice(name).end_cell().begin_parse();
+ if (op < 20) {
+ ;; lets resolve the name here so as not to duplicate the code
+ (slice pfx, cell val, slice tail, int succ) =
+ root.pfxdict_get_ref(1023, name);
+ if (succ) {
+ ;; must match EXACTLY to prevent accident changes
+ throw_unless(35, tail.slice_empty?());
+ cat_table = val;
+ }
+ ;; otherwise cat_table is null which is reasonable for actions
+ }
+ ;; 11 VSet: set specified value to specified subdomain->category
+ if (op == 11) {
+ cell new_value = ops~load_maybe_ref();
+ if (new_value.cell_null?()) {
+ cat_table~idict_delete?(16, cat);
+ } else {
+ cat_table~idict_set_ref(16, cat, new_value);
+ }
+ root~pfxdict_set_ref(1023, name, cat_table);
+ return (root, ops);
+ }
+ ;; 12 VDel: delete specified subdomain->category value
+ if (op == 12) {
+ ifnot (cat_table.dict_empty?()) {
+ cat_table~idict_delete?(16, cat);
+ root~pfxdict_set_ref(1023, name, cat_table);
+ }
+ return (root, ops);
+ }
+ ;; 21 DSet: replace entire category dictionary of domain with provided
+ if (op == 21) {
+ cell new_cat_table = ops~load_maybe_ref();
+ root~pfxdict_set_ref(1023, name, new_cat_table);
+ return (root, ops);
+ }
+ ;; 22 DDel: delete entire category dictionary of specified domain
+ if (op == 22) {
+ root~pfxdict_delete?(1023, name);
+ return (root, ops);
+ }
+ ;; 31 TSet: replace ENTIRE DOMAIN TABLE with the provided tree root cell
+ if (op == 31) {
+ cell new_tree_root = ops~load_maybe_ref();
+ ;; no sanity checks cause they would cost immense gas
+ return (new_tree_root, ops);
+ }
+ ;; 32 TDel: nullify ENTIRE DOMAIN TABLE
+ if (op == 32) {
+ return (null(), ops);
+ }
+ throw(44); ;; invalid operation
+ return (root, ops);
+}
+
+cell process_ops(cell root, slice ops) {
+ int f = -1;
+ while (f) {
+ (root, ops) = process_op(root, ops);
+ if (ops.slice_refs_empty?()) {
+ f = 0;
+ } else {
+ ops = ops~load_ref().begin_parse();
+ }
+ }
+ return root;
+}
+
+() recv_external(slice in_msg) impure {
+ ;; Load data
+ (int stored_subwalet, int last_cleaned, int public_key, cell root, cell old_queries) = load_data();
+
+ ;; validate signature and seqno
+ slice signature = in_msg~load_bits(512);
+ int shash = slice_hash(in_msg);
+ var (subwallet_id, query_id) = (in_msg~load_uint(32), in_msg~load_uint(64));
+ var bound = (now() << 32);
+ throw_if(35, query_id < bound);
+ (_, var found?) = old_queries.udict_get?(64, query_id);
+ throw_if(32, found?);
+ throw_unless(34, check_signature(shash, signature, public_key));
+ accept_message(); ;; message is signed by owner, sanity not guaranteed yet
+
+
+ int op = in_msg.preload_uint(6);
+ ;; 00 Contract initialization message (only if seqno = 0)
+ if (op == 0) {
+ ;; noop
+ } else { if (op == 51) {
+ in_msg~skip_bits(6);
+ public_key = in_msg~load_uint(256);
+ } else {
+ root = process_ops(root, in_msg);
+ } }
+
+ bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago
+ old_queries~udict_set_builder(64, query_id, begin_cell());
+ var queries = old_queries;
+ do {
+ var (old_queries', i, _, f) = old_queries.udict_delete_get_min(64);
+ f~touch();
+ if (f) {
+ f = (i < bound);
+ }
+ if (f) {
+ old_queries = old_queries';
+ last_cleaned = i;
+ }
+ } until (~ f);
+
+ store_data(subwallet_id, last_cleaned, public_key, root, old_queries);
+}
+
+{-
+ Data structure:
+ Root cell: [UInt<32b>:seqno] [UInt<256b>:owner_public_key]
+ [OptRef<1b+1r?>:HashmapCatTable>:domains]
+ := HashmapE 16 ^DNSRecord
+ := arbitary? not defined anywhere in documentation or internet!
+
+ STORED DOMAIN NAME SLICE FORMAT: (#ZeroChars<7b>) (Domain name value)
+ #Zeros allows to simultaneously store, for example, com\0 and com\0google\0
+ That will be stored as \1com\0 and \2com\0google\0 (pfx tree has restricitons)
+ This will allow to resolve more specific requests to subdomains, and resort
+ to parent domain next resolver lookup if subdomain is not found
+ com\0goo\0 lookup will, for example look up \2com\0goo\0 and then
+ \1com\0goo\0 which will return \1com\0 (as per pfx tree) with -1 cat
+-}
+
+;;===========================================================================;;
+;; Getter methods ;;
+;;===========================================================================;;
+
+int get_seqno() method_id { ;; Retrieve sequence number
+ return get_data().begin_parse().preload_uint(32);
+}
+
+;;8m dns-record-value
+(int, cell) dnsresolve(slice subdomain, int category) method_id {
+ cell Null = null(); ;; pseudo-alias
+ throw_if(30, subdomain.slice_bits() % 8 != 0); ;; malformed input (~ 8n-bit)
+ if (subdomain.slice_bits() == 0) { return (0, Null); } ;; zero-length input
+ {- ;; Logic thrown away: return only first ZB-delimited subdomain,
+ appends \0 if neccessary (not required for pfx, \0 can be ap more eff)
+ builder b_name = begin_cell();
+ slice remaining = subdomain;
+ int char = remaining~load_uint(8); ;; seems to be the most optimal way
+ do {
+ b_name~store_uint(char, 8);
+ char = remaining~load_uint(8);
+ } until ((remaining.slice_bits() == 0) | (char == 0));
+ if (char == 0) { category = -1; }
+ if ((remaining.slice_bits() == 0) & (char != 0)) {
+ b_name~store_uint(0, 8); ;; string was not terminated with zero byte
+ }
+ cell c_name = b_name.end_cell();
+ slice s_name = c_name.begin_parse();
+ -}
+ (_, int name_last_byte) = subdomain.slice_last(8).load_uint(8);
+ if ((name_last_byte == 0) & (subdomain.slice_bits() == 8)) {
+ return (0, Null); ;; zero-length input, but with zero byte
+ }
+ slice s_name = subdomain;
+ if (name_last_byte != 0) {
+ s_name = begin_cell().store_slice(subdomain) ;; append zero byte
+ .store_uint(0, 8).end_cell().begin_parse();
+ }
+ (_, _, _, cell root, _) = load_data();
+
+ ;; Multiple zero characters seem to be allowed as per github issue response
+ ;; Lets change the tactics!
+
+ int zeros = 0;
+ int loop = -1;
+ slice cname = s_name;
+ ;; better safe then sorry, dont want to catch any of loop bugs
+ while (loop) {
+ int lval = cname~load_uint(8);
+ if (lval == 0) { zeros += 1; }
+ if (cname.slice_bits() == 0) { loop = 0; }
+ }
+
+ ;; can't move below, will cause errors!
+ slice pfx = cname; cell val = null();
+ slice tail = cname; int succ = 0;
+
+ while (zeros > 0) {
+ slice pfname = begin_cell().store_uint(zeros, 7)
+ .store_slice(s_name).end_cell().begin_parse();
+ (pfx, val, tail, succ) = root.pfxdict_get_ref(1023, pfname);
+ if (succ) { zeros = 1; } ;; break
+ zeros -= 1;
+ }
+
+
+ zeros = pfx.preload_uint(7);
+
+ if (~ succ) {
+ return (0, Null); ;; failed to find entry in prefix dictionary
+ }
+ if (~ tail.slice_empty?()) { ;; if we have tail then len(pfx) < len(subdomain)
+ category = -1; ;; incomplete subdomain found, must return next resolver (-1)
+ }
+ int pfx_bits = pfx.slice_bits() - 7;
+ cell cat_table = val;
+ ;; pfx.slice_bits() will contain 8m, where m is number of bytes in subdomain
+ ;; COUNTING for the zero byte (if structurally correct: no multiple-ZB keys)
+ ;; which corresponds to "8m, m=one plus the number of bytes in the subdomain found)
+ if (category == 0) {
+ return (pfx_bits, cat_table); ;; return cell with entire dictionary for 0
+ } else {
+ cell cat_found = cat_table.idict_get_ref(16, category);
+ {- it seems that if subdomain is found but cat is not need to return (8m, Null)
+ if (cat_found.cell_null?()) {
+ pfx_bits = 0; ;; to return (0, Null) instead of (8m, Null)
+ ;; my thoughts about this requirement are in next block comment
+ } -}
+ return (pfx_bits, cat_found); ;; no need to unslice and cellize the poor cat now
+ {- Old logic garbage, replaced with ref functions discovered
+ ;; dictionary category lookup
+ (slice cat_value, int cat_found) = cat_table.idict_get?(16, category);
+ if (~ cat_found) {
+ ;; we have failed to find the cat :(
+ return (0, Null);
+ }
+ ;; cat is found, turn it's slices into cells
+ return (pfx.slice_bits(), begin_cell().store_slice(cat_value).end_cell());
+ -}
+ }
+}
diff --git a/crypto/smartcont/gen-zerostate.fif b/crypto/smartcont/gen-zerostate.fif
index 5c4de564..e0a86469 100644
--- a/crypto/smartcont/gen-zerostate.fif
+++ b/crypto/smartcont/gen-zerostate.fif
@@ -17,10 +17,10 @@ dup dup 31 boc+>B dup Bx. cr
dup "basestate0" +suffix +".boc" tuck B>file
."(Initial basechain state saved to file " type .")" cr
Bhashu dup =: basestate0_fhash
-."file hash=" dup x. space 256 u>B dup B>base64url type cr
+."file hash=" dup 64x. space 256 u>B dup B>base64url type cr
"basestate0" +suffix +".fhash" B>file
hashu dup =: basestate0_rhash
-."root hash=" dup x. space 256 u>B dup B>base64url type cr
+."root hash=" dup 64x. space 256 u>B dup B>base64url type cr
"basestate0" +suffix +".rhash" B>file
basestate0_rhash basestate0_fhash now 0 2 32 0 add-std-workchain
@@ -128,7 +128,7 @@ GR$666 // balance
2 // mode: create
register_smc
dup make_special dup constant smc3_addr
-."address = " x. cr
+."address = " 64x. cr
/*
*
@@ -160,7 +160,7 @@ Masterchain swap
// 9 4 1 config.validator_num!
1000 100 13 config.validator_num!
// min-stake max-stake min-total-stake max-factor
-GR$10000 GR$10000000 GR$500000 sg~10 config.validator_stake_limits!
+GR$10000 GR$10000000 GR$500000 sg~3 config.validator_stake_limits!
// elected-for elect-start-before elect-end-before stakes-frozen-for
// 400000 200000 4000 400000 config.election_params!
// 4000 2000 500 1000 config.election_params! // DEBUG
diff --git a/crypto/smartcont/show-addr.fif b/crypto/smartcont/show-addr.fif
index 506aa492..86b31463 100755
--- a/crypto/smartcont/show-addr.fif
+++ b/crypto/smartcont/show-addr.fif
@@ -12,9 +12,9 @@ $1 "new-wallet" replace-if-null =: file-base
file-base +".addr" dup ."Loading wallet address from " type cr file>B 32 B|
dup Blen { 32 B>i@ } { drop Basechain } cond constant wallet_wc
256 B>u@ dup constant wallet_addr
-."Source wallet address = " wallet_wc ._ .":" x. cr
-wallet_wc wallet_addr 2dup 7 smca>$ ."Non-bounceable address (for init only): " type cr
-6 smca>$ ."Bounceable address (for later access): " type cr
+."Source wallet address = " wallet_wc swap 2dup .addr cr
+."Non-bounceable address (for init only): " 2dup 7 .Addr cr
+."Bounceable address (for later access): " 6 .Addr cr
file-base +".pk" dup file-exists? {
dup file>B dup Blen 32 <> abort"Private key must be exactly 32 bytes long"
diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc
index 9041bafe..2dc5c137 100644
--- a/crypto/smartcont/stdlib.fc
+++ b/crypto/smartcont/stdlib.fc
@@ -31,6 +31,9 @@ int string_hash(slice s) asm "SHA256U";
int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU";
int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS";
+(int, int, int) compute_data_size(cell c, int max_cells) asm "CDATASIZE";
+(int, int, int) slice_compute_data_size(slice s, int max_cells) asm "SDATASIZE";
+
;; () throw_if(int excno, int cond) impure asm "THROWARGIF";
() dump_stack() impure asm "DUMPSTK";
diff --git a/crypto/smartcont/validator-elect-req.fif b/crypto/smartcont/validator-elect-req.fif
index 215fe9c1..a220b421 100755
--- a/crypto/smartcont/validator-elect-req.fif
+++ b/crypto/smartcont/validator-elect-req.fif
@@ -22,7 +22,7 @@ def? $5 { @' $5 } { "validator-to-sign.bin" } cond constant output_fname
."Creating a request to participate in validator elections at time " elect_time .
."from smart contract " -1 src_addr 2dup 1 .Addr ." = " .addr
." with maximal stake factor with respect to the minimal stake " max_factor ._
-."/65536 and validator ADNL address " adnl_addr x. cr
+."/65536 and validator ADNL address " adnl_addr 64x. cr
B{654c5074} elect_time 32 u>B B+ max_factor 32 u>B B+ src_addr 256 u>B B+ adnl_addr 256 u>B B+
dup Bx. cr
diff --git a/crypto/smartcont/validator-elect-signed.fif b/crypto/smartcont/validator-elect-signed.fif
index 55e3d9dd..4e681f07 100755
--- a/crypto/smartcont/validator-elect-signed.fif
+++ b/crypto/smartcont/validator-elect-signed.fif
@@ -27,7 +27,7 @@ def? $7 { @' $7 } { "validator-query.boc" } cond constant output_fname
."Creating a request to participate in validator elections at time " elect_time .
."from smart contract " -1 src_addr 2dup 1 .Addr ." = " .addr
." with maximal stake factor with respect to the minimal stake " max_factor ._
-."/65536 and validator ADNL address " adnl_addr x. cr
+."/65536 and validator ADNL address " adnl_addr 64x. cr
B{654c5074} elect_time 32 u>B B+ max_factor 32 u>B B+ src_addr 256 u>B B+ adnl_addr 256 u>B B+
."String to sign is: " dup Bx. cr constant to_sign
diff --git a/crypto/smc-envelope/HighloadWallet.cpp b/crypto/smc-envelope/HighloadWallet.cpp
index 5f540780..e44ce97b 100644
--- a/crypto/smc-envelope/HighloadWallet.cpp
+++ b/crypto/smc-envelope/HighloadWallet.cpp
@@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#include "HighloadWallet.h"
#include "GenericAccount.h"
@@ -51,7 +51,7 @@ td::Ref HighloadWallet::get_init_message(const td::Ed25519::PrivateKey
td::Ref HighloadWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
td::uint32 seqno, td::uint32 valid_until,
td::Span gifts) noexcept {
- CHECK(gifts.size() <= 254);
+ CHECK(gifts.size() <= max_gifts_size);
vm::Dictionary messages(16);
for (size_t i = 0; i < gifts.size(); i++) {
auto& gift = gifts[i];
@@ -64,7 +64,7 @@ td::Ref HighloadWallet::make_a_gift_message(const td::Ed25519::Private
vm::CellBuilder cb;
GenericAccount::store_int_message(cb, gift.destination, gramms);
cb.store_bytes("\0\0\0\0", 4);
- //vm::CellString::store(cb, gift.message, 35 * 8).ensure();
+ vm::CellString::store(cb, gift.message, 35 * 8).ensure();
auto message_inner = cb.finalize();
cb = {};
cb.store_long(send_mode, 8).store_ref(message_inner);
@@ -123,4 +123,20 @@ td::Result HighloadWallet::get_wallet_id_or_throw() const {
return static_cast(cs.fetch_ulong(32));
}
+td::Result HighloadWallet::get_public_key() const {
+ return TRY_VM(get_public_key_or_throw());
+}
+
+td::Result HighloadWallet::get_public_key_or_throw() const {
+ if (state_.data.is_null()) {
+ return td::Status::Error("data is null");
+ }
+ //FIXME use get method
+ auto cs = vm::load_cell_slice(state_.data);
+ cs.skip_first(64);
+ td::SecureString res(td::Ed25519::PublicKey::LENGTH);
+ cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast(res.size()));
+ return td::Ed25519::PublicKey(std::move(res));
+}
+
} // namespace ton
diff --git a/crypto/smc-envelope/HighloadWallet.h b/crypto/smc-envelope/HighloadWallet.h
index aa1e3a4b..e1db23ef 100644
--- a/crypto/smc-envelope/HighloadWallet.h
+++ b/crypto/smc-envelope/HighloadWallet.h
@@ -14,29 +14,26 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see .
- Copyright 2017-2019 Telegram Systems LLP
+ Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include "smc-envelope/SmartContract.h"
+#include "smc-envelope/WalletInterface.h"
#include "vm/cells.h"
#include "Ed25519.h"
#include "block/block.h"
#include "vm/cells/CellString.h"
namespace ton {
-class HighloadWallet : ton::SmartContract {
+class HighloadWallet : ton::SmartContract, public WalletInterface {
public:
explicit HighloadWallet(State state) : ton::SmartContract(std::move(state)) {
}
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
+ static constexpr unsigned max_gifts_size = 254;
static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept;
static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id) noexcept;
- struct Gift {
- block::StdAddress destination;
- td::int64 gramms;
- std::string message;
- };
static td::Ref make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
td::uint32 seqno, td::uint32 valid_until, td::Span gifts) noexcept;
@@ -47,8 +44,20 @@ class HighloadWallet : ton::SmartContract {
td::Result get_seqno() const;
td::Result get_wallet_id() const;
+ td::Result> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 valid_until,
+ td::Span gifts) const override {
+ TRY_RESULT(seqno, get_seqno());
+ TRY_RESULT(wallet_id, get_wallet_id());
+ return make_a_gift_message(private_key, wallet_id, seqno, valid_until, gifts);
+ }
+ size_t get_max_gifts_size() const override {
+ return max_gifts_size;
+ }
+ td::Result get_public_key() const override;
+
private:
td::Result get_seqno_or_throw() const;
td::Result get_wallet_id_or_throw() const;
+ td::Result get_public_key_or_throw() const;
};
} // namespace ton
diff --git a/crypto/smc-envelope/HighloadWalletV2.cpp b/crypto/smc-envelope/HighloadWalletV2.cpp
new file mode 100644
index 00000000..823161b9
--- /dev/null
+++ b/crypto/smc-envelope/HighloadWalletV2.cpp
@@ -0,0 +1,151 @@
+/*
+ This file is part of TON Blockchain Library.
+
+ TON Blockchain Library is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ TON Blockchain Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with TON Blockchain Library. If not, see .
+
+ Copyright 2017-2020 Telegram Systems LLP
+*/
+#include "HighloadWalletV2.h"
+#include "GenericAccount.h"
+#include "SmartContractCode.h"
+
+#include "vm/boc.h"
+#include "vm/cells/CellString.h"
+#include "td/utils/base64.h"
+
+#include
+
+namespace ton {
+td::optional HighloadWalletV2::guess_revision(const vm::Cell::Hash& code_hash) {
+ for (td::int32 i = 1; i <= 2; i++) {
+ if (get_init_code(i)->get_hash() == code_hash) {
+ return i;
+ }
+ }
+ return {};
+}
+td::optional HighloadWalletV2::guess_revision(const block::StdAddress& address,
+ const td::Ed25519::PublicKey& public_key,
+ td::uint32 wallet_id) {
+ for (td::int32 i = 1; i <= 2; i++) {
+ if (GenericAccount::get_address(address.workchain, get_init_state(public_key, wallet_id, i)) == address) {
+ return i;
+ }
+ }
+ return {};
+}
+td::Ref HighloadWalletV2::get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
+ td::int32 revision) noexcept {
+ auto code = get_init_code(revision);
+ auto data = get_init_data(public_key, wallet_id);
+ return GenericAccount::get_init_state(std::move(code), std::move(data));
+}
+
+td::Ref HighloadWalletV2::get_init_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
+ td::uint32 valid_until) noexcept {
+ td::uint32 id = -1;
+ auto append_message = [&](auto&& cb) -> vm::CellBuilder& {
+ cb.store_long(wallet_id, 32).store_long(valid_until, 32).store_long(id, 32);
+ CHECK(cb.store_maybe_ref({}));
+ return cb;
+ };
+ auto signature = private_key.sign(append_message(vm::CellBuilder()).finalize()->get_hash().as_slice()).move_as_ok();
+
+ return append_message(vm::CellBuilder().store_bytes(signature)).finalize();
+}
+
+td::Ref HighloadWalletV2::make_a_gift_message(const td::Ed25519::PrivateKey& private_key,
+ td::uint32 wallet_id, td::uint32 valid_until,
+ td::Span gifts) noexcept {
+ CHECK(gifts.size() <= max_gifts_size);
+ vm::Dictionary messages(16);
+ for (size_t i = 0; i < gifts.size(); i++) {
+ auto& gift = gifts[i];
+ td::int32 send_mode = 3;
+ auto gramms = gift.gramms;
+ if (gramms == -1) {
+ gramms = 0;
+ send_mode += 128;
+ }
+ vm::CellBuilder cb;
+ GenericAccount::store_int_message(cb, gift.destination, gramms);
+ cb.store_bytes("\0\0\0\0", 4);
+ vm::CellString::store(cb, gift.message, 35 * 8).ensure();
+ auto message_inner = cb.finalize();
+ cb = {};
+ cb.store_long(send_mode, 8).store_ref(message_inner);
+ auto key = messages.integer_key(td::make_refint(i), 16, false);
+ messages.set_builder(key.bits(), 16, cb);
+ }
+ std::string hash;
+ {
+ vm::CellBuilder cb;
+ CHECK(cb.store_maybe_ref(messages.get_root_cell()));
+ hash = cb.finalize()->get_hash().as_slice().substr(28, 4).str();
+ }
+
+ vm::CellBuilder cb;
+ cb.store_long(wallet_id, 32).store_long(valid_until, 32).store_bytes(hash);
+ CHECK(cb.store_maybe_ref(messages.get_root_cell()));
+ auto message_outer = cb.finalize();
+ auto signature = private_key.sign(message_outer->get_hash().as_slice()).move_as_ok();
+ return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
+}
+
+td::Ref HighloadWalletV2::get_init_code(td::int32 revision) noexcept {
+ return SmartContractCode::highload_wallet_v2(revision);
+}
+
+vm::CellHash HighloadWalletV2::get_init_code_hash() noexcept {
+ return get_init_code(0)->get_hash();
+}
+
+td::Ref HighloadWalletV2::get_init_data(const td::Ed25519::PublicKey& public_key,
+ td::uint32 wallet_id) noexcept {
+ vm::CellBuilder cb;
+ cb.store_long(wallet_id, 32).store_long(0, 64).store_bytes(public_key.as_octet_string());
+ CHECK(cb.store_maybe_ref({}));
+ return cb.finalize();
+}
+
+td::Result HighloadWalletV2::get_wallet_id() const {
+ return TRY_VM(get_wallet_id_or_throw());
+}
+
+td::Result HighloadWalletV2::get_wallet_id_or_throw() const {
+ if (state_.data.is_null()) {
+ return 0;
+ }
+ //FIXME use get method
+ auto cs = vm::load_cell_slice(state_.data);
+ return static_cast(cs.fetch_ulong(32));
+}
+
+td::Result HighloadWalletV2::get_public_key() const {
+ return TRY_VM(get_public_key_or_throw());
+}
+
+td::Result HighloadWalletV2::get_public_key_or_throw() const {
+ if (state_.data.is_null()) {
+ return td::Status::Error("data is null");
+ }
+ //FIXME use get method
+ auto cs = vm::load_cell_slice(state_.data);
+ cs.skip_first(96);
+ td::SecureString res(td::Ed25519::PublicKey::LENGTH);
+ cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast(res.size()));
+ return td::Ed25519::PublicKey(std::move(res));
+}
+
+} // namespace ton
diff --git a/crypto/smc-envelope/HighloadWalletV2.h b/crypto/smc-envelope/HighloadWalletV2.h
new file mode 100644
index 00000000..2b6f99e7
--- /dev/null
+++ b/crypto/smc-envelope/HighloadWalletV2.h
@@ -0,0 +1,66 @@
+/*
+ This file is part of TON Blockchain Library.
+
+ TON Blockchain Library is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ TON Blockchain Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with TON Blockchain Library. If not, see .
+
+ Copyright 2017-2020 Telegram Systems LLP
+*/
+#pragma once
+
+#include "smc-envelope/SmartContract.h"
+#include "smc-envelope/WalletInterface.h"
+#include "vm/cells.h"
+#include "Ed25519.h"
+#include "block/block.h"
+#include "vm/cells/CellString.h"
+
+namespace ton {
+class HighloadWalletV2 : ton::SmartContract, public WalletInterface {
+ public:
+ explicit HighloadWalletV2(State state) : ton::SmartContract(std::move(state)) {
+ }
+ static constexpr unsigned max_message_size = vm::CellString::max_bytes;
+ static constexpr unsigned max_gifts_size = 254;
+ static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
+ td::int32 revision) noexcept;
+ static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
+ td::uint32 valid_until) noexcept;
+
+ static td::Ref make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
+ td::uint32 valid_until, td::Span gifts) noexcept;
+
+ static td::Ref get_init_code(td::int32 revision) noexcept;
+ static vm::CellHash get_init_code_hash() noexcept;
+ static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept;
+ static td::optional guess_revision(const vm::Cell::Hash& code_hash);
+ static td::optional guess_revision(const block::StdAddress& address,
+ const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id);
+
+ td::Result get_wallet_id() const;
+
+ td::Result> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 valid_until,
+ td::Span gifts) const override {
+ TRY_RESULT(wallet_id, get_wallet_id());
+ return make_a_gift_message(private_key, wallet_id, valid_until, gifts);
+ }
+ size_t get_max_gifts_size() const override {
+ return max_gifts_size;
+ }
+ td::Result get_public_key() const override;
+
+ private:
+ td::Result get_wallet_id_or_throw() const;
+ td::Result get_public_key_or_throw() const;
+};
+} // namespace ton
diff --git a/crypto/smc-envelope/ManualDns.cpp b/crypto/smc-envelope/ManualDns.cpp
new file mode 100644
index 00000000..c932f49d
--- /dev/null
+++ b/crypto/smc-envelope/ManualDns.cpp
@@ -0,0 +1,542 @@
+/*
+ This file is part of TON Blockchain Library.
+
+ TON Blockchain Library is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ TON Blockchain Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with TON Blockchain Library. If not, see .
+
+ Copyright 2019-2020 Telegram Systems LLP
+*/
+#include "ManualDns.h"
+
+#include "smc-envelope/SmartContractCode.h"
+
+#include "vm/dict.h"
+
+#include "td/utils/format.h"
+#include "td/utils/overloaded.h"
+#include "td/utils/Parser.h"
+#include "td/utils/Random.h"
+
+#include "block/block-auto.h"
+#include "block/block-parse.h"
+
+namespace ton {
+
+//proto_list_nil$0 = ProtoList;
+//proto_list_next$1 head:Protocol tail:ProtoList = ProtoList;
+//proto_http#4854 = Protocol;
+
+//cap_list_nil$0 = SmcCapList;
+//cap_list_next$1 head:SmcCapability tail:SmcCapList = SmcCapList;
+//cap_method_seqno#5371 = SmcCapability;
+//cap_method_pubkey#71f4 = SmcCapability;
+//cap_is_wallet#2177 = SmcCapability;
+//cap_name#ff name:Text = SmcCapability;
+//
+td::Result> DnsInterface::EntryData::as_cell() const {
+ td::Ref res;
+ td::Status error;
+ data.visit(td::overloaded(
+ [&](const EntryDataText& text) {
+ block::gen::DNSRecord::Record_dns_text dns;
+ vm::CellBuilder cb;
+ vm::CellText::store(cb, text.text);
+ dns.x = vm::load_cell_slice_ref(cb.finalize());
+ tlb::pack_cell(res, dns);
+ },
+ [&](const EntryDataNextResolver& resolver) {
+ block::gen::DNSRecord::Record_dns_next_resolver dns;
+ vm::CellBuilder cb;
+ block::tlb::t_MsgAddressInt.store_std_address(cb, resolver.resolver.workchain, resolver.resolver.addr);
+ dns.resolver = vm::load_cell_slice_ref(cb.finalize());
+ tlb::pack_cell(res, dns);
+ },
+ [&](const EntryDataAdnlAddress& adnl_address) {
+ block::gen::DNSRecord::Record_dns_adnl_address dns;
+ dns.adnl_addr = adnl_address.adnl_address;
+ dns.flags = 0;
+ tlb::pack_cell(res, dns);
+ },
+ [&](const EntryDataSmcAddress& smc_address) {
+ block::gen::DNSRecord::Record_dns_smc_address dns;
+ vm::CellBuilder cb;
+ block::tlb::t_MsgAddressInt.store_std_address(cb, smc_address.smc_address.workchain,
+ smc_address.smc_address.addr);
+ dns.smc_addr = vm::load_cell_slice_ref(cb.finalize());
+ tlb::pack_cell(res, dns);
+ }));
+ if (error.is_error()) {
+ return error;
+ }
+ if (res.is_null()) {
+ return td::Status::Error("Entry data is emtpy");
+ }
+ return res;
+ //dns_text#1eda _:Text = DNSRecord;
+
+ //dns_next_resolver#ba93 resolver:MsgAddressInt = DNSRecord; // usually in record #-1
+ //dns_adnl_address#ad01 adnl_addr:bits256 flags:(## 8) { flags <= 1 } proto_list:flags . 0?ProtoList = DNSRecord; // often in record #2
+
+ //dns_smc_address#9fd3 smc_addr:MsgAddressInt flags:(## 8) { flags <= 1 } cap_list:flags . 0?SmcCapList = DNSRecord; // often in record #1
+}
+
+td::Result DnsInterface::EntryData::from_cellslice(vm::CellSlice& cs) {
+ switch (block::gen::t_DNSRecord.get_tag(cs)) {
+ case block::gen::DNSRecord::dns_text: {
+ block::gen::DNSRecord::Record_dns_text dns;
+ tlb::unpack(cs, dns);
+ TRY_RESULT(text, vm::CellText::load(dns.x.write()));
+ return EntryData::text(std::move(text));
+ }
+ case block::gen::DNSRecord::dns_next_resolver: {
+ block::gen::DNSRecord::Record_dns_next_resolver dns;
+ tlb::unpack(cs, dns);
+ ton::WorkchainId wc;
+ ton::StdSmcAddress addr;
+ if (!block::tlb::t_MsgAddressInt.extract_std_address(dns.resolver, wc, addr)) {
+ return td::Status::Error("Invalid address");
+ }
+ return EntryData::next_resolver(block::StdAddress(wc, addr));
+ }
+ case block::gen::DNSRecord::dns_adnl_address: {
+ block::gen::DNSRecord::Record_dns_adnl_address dns;
+ tlb::unpack(cs, dns);
+ return EntryData::adnl_address(dns.adnl_addr);
+ }
+ case block::gen::DNSRecord::dns_smc_address: {
+ block::gen::DNSRecord::Record_dns_smc_address dns;
+ tlb::unpack(cs, dns);
+ ton::WorkchainId wc;
+ ton::StdSmcAddress addr;
+ if (!block::tlb::t_MsgAddressInt.extract_std_address(dns.smc_addr, wc, addr)) {
+ return td::Status::Error("Invalid address");
+ }
+ return EntryData::smc_address(block::StdAddress(wc, addr));
+ }
+ }
+ return td::Status::Error("Unknown entry data");
+}
+
+td::Result> DnsInterface::resolve(td::Slice name, td::int32 category) const {
+ TRY_RESULT(raw_entries, resolve_raw(name, category));
+ std::vector entries;
+ entries.reserve(raw_entries.size());
+ for (auto& raw_entry : raw_entries) {
+ Entry entry;
+ entry.name = std::move(raw_entry.name);
+ entry.category = raw_entry.category;
+ auto cs = vm::load_cell_slice(raw_entry.data);
+ TRY_RESULT(data, EntryData::from_cellslice(cs));
+ entry.data = std::move(data);
+ entries.push_back(std::move(entry));
+ }
+ return entries;
+}
+
+/*
+ External message structure:
+ [Bytes<512b>:signature] [UInt<32b>:seqno] [UInt<6b>:operation]
+ [Either b0: inline name (<= 58-x Bytes) or b1: reference-stored name)
+ x depends on operation
+ Use of 6-bit op instead of 32-bit allows to save 4 bytes for inline name
+ Inline [Name] structure: [UInt<6b>:length] [Bytes:data]
+ Operations (continuation of message):
+ 00 Contract initialization message (only if seqno = 0) (x=-)
+ 31 TSet: replace ENTIRE DOMAIN TABLE with the provided tree root cell (x=-)
+ [Cell<1r>:new_domains_table]
+ 51 OSet: replace owner public key with a new one (x=-)
+ [UInt<256b>:new_public_key]
+*/
+// creation
+td::Ref ManualDns::create(td::Ref data) {
+ return td::Ref(true, State{ton::SmartContractCode::dns_manual(), std::move(data)});
+}
+td::Ref ManualDns::create(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) {
+ return create(create_init_data_fast(public_key, wallet_id));
+}
+
+td::Result ManualDns::get_wallet_id() const {
+ return TRY_VM(get_wallet_id_or_throw());
+}
+td::Result ManualDns::get_wallet_id_or_throw() const {
+ if (state_.data.is_null()) {
+ return 0;
+ }
+ //FIXME use get method
+ return static_cast(vm::load_cell_slice(state_.data).fetch_ulong(32));
+}
+
+td::Result> ManualDns::create_set_value_unsigned(td::int16 category, td::Slice name,
+ td::Ref data) const {
+ //11 VSet: set specified value to specified subdomain->category (x=2)
+ //[Int<16b>:category] [Name>:subdomain] [Cell<1r>:value]
+ vm::CellBuilder cb;
+ cb.store_long(11, 6);
+ if (name.size() <= 58 - 2) {
+ cb.store_long(0, 1);
+ cb.store_long(category, 16);
+ cb.store_long(name.size(), 6);
+ cb.store_bytes(name);
+ } else {
+ cb.store_long(1, 1);
+ cb.store_long(category, 16);
+ cb.store_ref(vm::CellBuilder().store_bytes(name).finalize());
+ }
+ cb.store_maybe_ref(std::move(data));
+ return cb.finalize();
+}
+td::Result> ManualDns::create_delete_value_unsigned(td::int16 category, td::Slice name) const {
+ //12 VDel: delete specified subdomain->category (x=2)
+ //[Int<16b>:category] [Name>:subdomain]
+ vm::CellBuilder cb;
+ cb.store_long(12, 6);
+ if (name.size() <= 58 - 2) {
+ cb.store_long(0, 1);
+ cb.store_long(category, 16);
+ cb.store_long(name.size(), 6);
+ cb.store_bytes(name);
+ } else {
+ cb.store_long(1, 1);
+ cb.store_long(category, 16);
+ cb.store_ref(vm::CellBuilder().store_bytes(name).finalize());
+ }
+ cb.store_long(0, 1);
+ return cb.finalize();
+}
+
+td::Result> ManualDns::create_delete_all_unsigned() const {
+ // 32 TDel: nullify ENTIRE DOMAIN TABLE (x=-)
+ vm::CellBuilder cb;
+ cb.store_long(32, 6);
+ cb.store_long(0, 1);
+ return cb.finalize();
+}
+
+td::Result> ManualDns::create_set_all_unsigned(td::Span entries) const {
+ vm::PrefixDictionary pdict(1023);
+ for (auto& action : entries) {
+ auto name_key = encode_name(action.name);
+ int zero_cnt = 0;
+ for (auto c : name_key) {
+ if (c == 0) {
+ zero_cnt++;
+ }
+ }
+ auto new_name_key = vm::load_cell_slice(vm::CellBuilder().store_long(zero_cnt, 7).store_bytes(name_key).finalize());
+ auto ptr = new_name_key.data_bits();
+ auto ptr_size = new_name_key.size();
+ auto o_dict = pdict.lookup(ptr, ptr_size);
+ td::Ref dict_root;
+ if (o_dict.not_null()) {
+ o_dict->prefetch_maybe_ref(dict_root);
+ }
+ vm::Dictionary dict(dict_root, 16);
+ if (!action.data.value().is_null()) {
+ auto key = dict.integer_key(td::make_refint(action.category), 16);
+ dict.set_ref(key.bits(), 16, action.data.value());
+ }
+ pdict.set(ptr, ptr_size, dict.get_root());
+ }
+
+ vm::CellBuilder cb;
+ cb.store_long(31, 6);
+ cb.store_long(1, 1);
+
+ cb.store_maybe_ref(pdict.get_root_cell());
+
+ return cb.finalize();
+}
+
+//21 DSet: replace entire category dictionary of domain with provided (x=0)
+//[Name>:subdomain] [Cell<1r>:new_cat_table]
+//22 DDel: delete entire category dictionary of specified domain (x=0)
+//[Name>:subdomain]
+td::Result> ManualDns::create_delete_name_unsigned(td::Slice name) const {
+ vm::CellBuilder cb;
+ cb.store_long(22, 6);
+ if (name.size() <= 58) {
+ cb.store_long(0, 1);
+ cb.store_long(name.size(), 6);
+ cb.store_bytes(name);
+ } else {
+ cb.store_long(1, 1);
+ cb.store_ref(vm::CellBuilder().store_bytes(name).finalize());
+ }
+ cb.store_long(0, 1);
+ return cb.finalize();
+}
+td::Result> ManualDns::create_set_name_unsigned(td::Slice name, td::Span entries) const {
+ vm::CellBuilder cb;
+ cb.store_long(21, 6);
+ if (name.size() <= 58) {
+ cb.store_long(0, 1);
+ cb.store_long(name.size(), 6);
+ cb.store_bytes(name);
+ } else {
+ cb.store_long(1, 1);
+ cb.store_ref(vm::CellBuilder().store_bytes(name).finalize());
+ }
+
+ vm::Dictionary dict(16);
+
+ for (auto& action : entries) {
+ if (action.data.value().is_null()) {
+ continue;
+ }
+ auto key = dict.integer_key(td::make_refint(action.category), 16);
+ dict.set_ref(key.bits(), 16, action.data.value());
+ }
+ cb.store_maybe_ref(dict.get_root_cell());
+
+ return cb.finalize();
+}
+
+td::Result> ManualDns::prepare(td::Ref data, td::uint32 valid_until) const {
+ TRY_RESULT(wallet_id, get_wallet_id());
+ auto hash = data->get_hash().as_slice().substr(28, 4).str();
+
+ vm::CellBuilder cb;
+ cb.store_long(wallet_id, 32).store_long(valid_until, 32);
+ //cb.store_bytes(hash);
+ cb.store_long(td::Random::secure_uint32(), 32);
+ cb.append_cellslice(vm::load_cell_slice(data));
+ return cb.finalize();
+}
+
+td::Result> ManualDns::sign(const td::Ed25519::PrivateKey& private_key, td::Ref data) {
+ auto signature = private_key.sign(data->get_hash().as_slice()).move_as_ok();
+ vm::CellBuilder cb;
+ cb.store_bytes(signature.as_slice());
+ cb.append_cellslice(vm::load_cell_slice(data));
+ return cb.finalize();
+}
+
+td::Result> ManualDns::create_init_query(const td::Ed25519::PrivateKey& private_key,
+ td::uint32 valid_until) const {
+ vm::CellBuilder cb;
+ cb.store_long(0, 6);
+ cb.store_long(0, 1);
+
+ TRY_RESULT(prepared, prepare(cb.finalize(), valid_until));
+ return sign(private_key, std::move(prepared));
+}
+
+td::Ref ManualDns::create_init_data_fast(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) {
+ vm::CellBuilder cb;
+ cb.store_long(wallet_id, 32).store_long(0, 64).store_bytes(public_key.as_octet_string());
+ CHECK(cb.store_maybe_ref({}));
+ CHECK(cb.store_maybe_ref({}));
+ return cb.finalize();
+}
+
+size_t ManualDns::get_max_name_size() const {
+ return 128;
+}
+
+td::Result> ManualDns::resolve_raw(td::Slice name, td::int32 category_big) const {
+ return TRY_VM(resolve_raw_or_throw(name, category_big));
+}
+td::Result> ManualDns::resolve_raw_or_throw(td::Slice name,
+ td::int32 category_big) const {
+ TRY_RESULT(category, td::narrow_cast_safe(category_big));
+ if (name.size() > get_max_name_size()) {
+ return td::Status::Error("Name is too long");
+ }
+ auto encoded_name = encode_name(name);
+ auto res = run_get_method(
+ "dnsresolve",
+ {vm::load_cell_slice_ref(vm::CellBuilder().store_bytes(encoded_name).finalize()), td::make_refint(category)});
+ if (!res.success) {
+ return td::Status::Error("get method failed");
+ }
+ std::vector vec;
+ auto data = res.stack.write().pop_maybe_cell();
+ if (data.is_null()) {
+ return vec;
+ }
+ size_t prefix_size = res.stack.write().pop_smallint_range((int)encoded_name.size() * 8);
+ if (prefix_size % 8 != 0) {
+ return td::Status::Error("Prefix size is not divisible by 8");
+ }
+ prefix_size /= 8;
+ if (prefix_size < encoded_name.size()) {
+ vec.push_back({decode_name(td::Slice(encoded_name).substr(0, prefix_size)), -1, data});
+ } else {
+ if (category == 0) {
+ vm::Dictionary dict(std::move(data), 16);
+ dict.check_for_each([&](auto cs, auto x, auto y) {
+ td::BigInt256 cat;
+ cat.import_bits(x, y, true);
+ vec.push_back({name.str(), td::narrow_cast(cat.to_long()), cs->prefetch_ref()});
+ return true;
+ });
+ } else {
+ vec.push_back({name.str(), category, data});
+ }
+ }
+
+ return vec;
+}
+
+td::Result> ManualDns::create_update_query(CombinedActions& combined) const {
+ if (combined.name.empty()) {
+ if (combined.actions.value().empty()) {
+ return create_delete_all_unsigned();
+ }
+ return create_set_all_unsigned(combined.actions.value());
+ }
+ if (combined.category == 0) {
+ if (!combined.actions) {
+ return create_delete_name_unsigned(encode_name(combined.name));
+ }
+ return create_set_name_unsigned(encode_name(combined.name), combined.actions.value());
+ }
+ CHECK(combined.actions.value().size() == 1);
+ auto& action = combined.actions.value()[0];
+ if (action.data) {
+ return create_set_value_unsigned(action.category, encode_name(action.name), action.data.value());
+ } else {
+ return create_delete_value_unsigned(action.category, encode_name(action.name));
+ }
+}
+
+td::Result> ManualDns::create_update_query(td::Ed25519::PrivateKey& pk, td::Span actions,
+ td::uint32 valid_until) const {
+ auto combined = combine_actions(actions);
+ std::vector> queries;
+ for (auto& c : combined) {
+ TRY_RESULT(q, create_update_query(c));
+ queries.push_back(std::move(q));
+ }
+
+ td::Ref combined_query;
+ for (auto& query : td::reversed(queries)) {
+ if (combined_query.is_null()) {
+ combined_query = std::move(query);
+ } else {
+ auto next = vm::load_cell_slice(combined_query);
+ combined_query = vm::CellBuilder()
+ .append_cellslice(vm::load_cell_slice(query))
+ .store_ref(vm::CellBuilder().append_cellslice(next).finalize())
+ .finalize();
+ }
+ }
+
+ TRY_RESULT(prepared, prepare(std::move(combined_query), valid_until));
+ return sign(pk, std::move(prepared));
+}
+
+std::string ManualDns::encode_name(td::Slice name) {
+ std::string res;
+ while (!name.empty()) {
+ auto pos = name.rfind('.');
+ if (pos == name.npos) {
+ res += name.str();
+ name = td::Slice();
+ } else {
+ res += name.substr(pos + 1).str();
+ name.truncate(pos);
+ }
+ res += '\0';
+ }
+ return res;
+}
+
+std::string ManualDns::decode_name(td::Slice name) {
+ std::string res;
+ if (!name.empty() && name.back() == 0) {
+ name.remove_suffix(1);
+ }
+ while (!name.empty()) {
+ auto pos = name.rfind('\0');
+ if (!res.empty()) {
+ res += '.';
+ }
+ if (pos == name.npos) {
+ res += name.str();
+ name = td::Slice();
+ } else {
+ res += name.substr(pos + 1).str();
+ name.truncate(pos);
+ }
+ }
+ return res;
+}
+
+std::string ManualDns::serialize_data(const EntryData& data) {
+ std::string res;
+ data.data.visit(td::overloaded([&](const ton::ManualDns::EntryDataText& text) { res = "UNSUPPORTED"; },
+ [&](const ton::ManualDns::EntryDataNextResolver& resolver) { res = "UNSUPPORTED"; },
+ [&](const ton::ManualDns::EntryDataAdnlAddress& adnl_address) { res = "UNSUPPORTED"; },
+ [&](const ton::ManualDns::EntryDataSmcAddress& text) { res = "UNSUPPORTED"; }));
+ return res;
+}
+
+td::Result> ManualDns::parse_data(td::Slice cmd) {
+ td::ConstParser parser(cmd);
+ parser.skip_whitespaces();
+ auto type = parser.read_till(':');
+ parser.advance(1);
+ if (type == "TEXT") {
+ return ManualDns::EntryData::text(parser.read_all().str());
+ } else if (type == "DELETED") {
+ return {};
+ }
+ return td::Status::Error(PSLICE() << "Unknown entry type: " << type);
+}
+
+td::Result ManualDns::parse_line(td::Slice cmd) {
+ // Cmd =
+ // set name category data |
+ // delete.name name |
+ // delete.all
+ // data =
+ // TEXT: |
+ // DELETED
+ td::ConstParser parser(cmd);
+ auto type = parser.read_word();
+ if (type == "set") {
+ auto name = parser.read_word();
+ auto category_str = parser.read_word();
+ TRY_RESULT(category, td::to_integer_safe(category_str));
+ TRY_RESULT(data, parse_data(parser.read_all()));
+ return ManualDns::ActionExt{name.str(), category, std::move(data)};
+ } else if (type == "delete.name") {
+ auto name = parser.read_word();
+ if (name.empty()) {
+ return td::Status::Error("name is empty");
+ }
+ return ManualDns::ActionExt{name.str(), 0, {}};
+ } else if (type == "delete.all") {
+ return ManualDns::ActionExt{"", 0, {}};
+ }
+ return td::Status::Error(PSLICE() << "Unknown command: " << type);
+}
+
+td::Result> ManualDns::parse(td::Slice cmd) {
+ auto lines = td::full_split(cmd, '\n');
+ std::vector res;
+ res.reserve(lines.size());
+ for (auto& line : lines) {
+ td::ConstParser parser(line);
+ parser.skip_whitespaces();
+ if (parser.empty()) {
+ continue;
+ }
+ TRY_RESULT(action, parse_line(parser.read_all()));
+ res.push_back(std::move(action));
+ }
+ return res;
+}
+
+} // namespace ton
diff --git a/crypto/smc-envelope/ManualDns.h b/crypto/smc-envelope/ManualDns.h
new file mode 100644
index 00000000..08db188e
--- /dev/null
+++ b/crypto/smc-envelope/ManualDns.h
@@ -0,0 +1,339 @@
+/*
+ This file is part of TON Blockchain Library.
+
+ TON Blockchain Library is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ TON Blockchain Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with TON Blockchain Library. If not, see .
+
+ Copyright 2019-2020 Telegram Systems LLP
+*/
+#pragma once
+#include "td/utils/Variant.h"
+#include "td/utils/Status.h"
+#include "vm/cells/Cell.h"
+#include "vm/cells/CellSlice.h"
+#include "vm/cells/CellString.h"
+
+#include "smc-envelope/SmartContract.h"
+
+#include "Ed25519.h"
+
+#include