mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
Merge branch 'testnet' into block-generation
# Conflicts: # overlay/overlay-fec-broadcast.cpp # overlay/overlay.hpp # tl/generate/scheme/ton_api.tl # tl/generate/scheme/ton_api.tlo # validator-engine-console/validator-engine-console-query.cpp # validator-engine-console/validator-engine-console-query.h # validator-engine-console/validator-engine-console.cpp # validator-engine/validator-engine.cpp # validator-engine/validator-engine.hpp # validator/downloaders/wait-block-state.hpp # validator/impl/validate-query.cpp # validator/validator.h
This commit is contained in:
commit
636348ffc2
110 changed files with 3822 additions and 696 deletions
79
.github/script/fift-func-wasm-build-ubuntu.sh
vendored
Executable file
79
.github/script/fift-func-wasm-build-ubuntu.sh
vendored
Executable file
|
@ -0,0 +1,79 @@
|
||||||
|
# The script build funcfift compiler to WASM
|
||||||
|
|
||||||
|
# dependencies:
|
||||||
|
#sudo apt-get install -y build-essential git make cmake clang libgflags-dev zlib1g-dev libssl-dev libreadline-dev libmicrohttpd-dev pkg-config libgsl-dev python3 python3-dev python3-pip nodejs
|
||||||
|
|
||||||
|
export CC=$(which clang)
|
||||||
|
export CXX=$(which clang++)
|
||||||
|
export CCACHE_DISABLE=1
|
||||||
|
|
||||||
|
git clone https://github.com/openssl/openssl.git
|
||||||
|
cd openssl
|
||||||
|
git checkout OpenSSL_1_1_1j
|
||||||
|
|
||||||
|
./config
|
||||||
|
make -j4
|
||||||
|
|
||||||
|
OPENSSL_DIR=`pwd`
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
git clone https://github.com/madler/zlib.git
|
||||||
|
cd zlib
|
||||||
|
ZLIB_DIR=`pwd`
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# clone ton repo
|
||||||
|
git clone --recursive https://github.com/the-ton-tech/ton-blockchain.git
|
||||||
|
|
||||||
|
# only to generate auto-block.cpp
|
||||||
|
|
||||||
|
cd ton-blockchain
|
||||||
|
git pull
|
||||||
|
git checkout 1566a23b2bece49fd1de9ab2f35e88297d22829f
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Release -DZLIB_LIBRARY=/usr/lib/x86_64-linux-gnu/libz.so -DZLIB_INCLUDE_DIR=$ZLIB_DIR -DOPENSSL_ROOT_DIR=$OPENSSL_DIR -DOPENSSL_INCLUDE_DIR=$OPENSSL_DIR/include -DOPENSSL_CRYPTO_LIBRARY=$OPENSSL_DIR/libcrypto.so -DOPENSSL_SSL_LIBRARY=$OPENSSL_DIR/libssl.so ..
|
||||||
|
make -j4 fift
|
||||||
|
|
||||||
|
rm -rf *
|
||||||
|
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
git clone https://github.com/emscripten-core/emsdk.git
|
||||||
|
cd emsdk
|
||||||
|
./emsdk install latest
|
||||||
|
./emsdk activate latest
|
||||||
|
EMSDK_DIR=`pwd`
|
||||||
|
|
||||||
|
source $EMSDK_DIR/emsdk_env.sh
|
||||||
|
export CC=$(which emcc)
|
||||||
|
export CXX=$(which em++)
|
||||||
|
export CCACHE_DISABLE=1
|
||||||
|
|
||||||
|
cd ../zlib
|
||||||
|
|
||||||
|
emconfigure ./configure --static
|
||||||
|
emmake make -j4
|
||||||
|
ZLIB_DIR=`pwd`
|
||||||
|
|
||||||
|
cd ../openssl
|
||||||
|
|
||||||
|
make clean
|
||||||
|
emconfigure ./Configure linux-generic32 no-shared no-dso no-engine no-unit-test no-ui
|
||||||
|
sed -i 's/CROSS_COMPILE=.*/CROSS_COMPILE=/g' Makefile
|
||||||
|
sed -i 's/-ldl//g' Makefile
|
||||||
|
sed -i 's/-O3/-Os/g' Makefile
|
||||||
|
emmake make depend
|
||||||
|
emmake make -j4
|
||||||
|
|
||||||
|
cd ../ton-blockchain
|
||||||
|
|
||||||
|
cd build
|
||||||
|
|
||||||
|
emcmake cmake -DUSE_EMSCRIPTEN=ON -DCMAKE_BUILD_TYPE=Release -DZLIB_LIBRARY=$ZLIB_DIR/libz.a -DZLIB_INCLUDE_DIR=$ZLIB_DIR -DOPENSSL_ROOT_DIR=$OPENSSL_DIR -DOPENSSL_INCLUDE_DIR=$OPENSSL_DIR/include -DOPENSSL_CRYPTO_LIBRARY=$OPENSSL_DIR/libcrypto.a -DOPENSSL_SSL_LIBRARY=$OPENSSL_DIR/libssl.a -DCMAKE_TOOLCHAIN_FILE=$EMSDK_DIR/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_CXX_FLAGS="-pthread -sUSE_ZLIB=1" ..
|
||||||
|
|
||||||
|
cp -R ../crypto/smartcont ../crypto/fift/lib crypto
|
||||||
|
|
||||||
|
emmake make -j4 funcfiftlib
|
|
@ -84,7 +84,11 @@ set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||||
set(CMAKE_CXX_EXTENSIONS FALSE)
|
set(CMAKE_CXX_EXTENSIONS FALSE)
|
||||||
|
|
||||||
#BEGIN internal
|
#BEGIN internal
|
||||||
|
option(USE_EMSCRIPTEN "Use \"ON\" for config building wasm." OFF)
|
||||||
option(TON_ONLY_TONLIB "Use \"ON\" to build only tonlib." OFF)
|
option(TON_ONLY_TONLIB "Use \"ON\" to build only tonlib." OFF)
|
||||||
|
if (USE_EMSCRIPTEN)
|
||||||
|
set(TON_ONLY_TONLIB true)
|
||||||
|
endif()
|
||||||
if (TON_ONLY_TONLIB)
|
if (TON_ONLY_TONLIB)
|
||||||
set(NOT_TON_ONLY_TONLIB false)
|
set(NOT_TON_ONLY_TONLIB false)
|
||||||
else()
|
else()
|
||||||
|
@ -197,10 +201,13 @@ find_package(Threads REQUIRED)
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
|
|
||||||
if (TON_ARCH AND NOT MSVC)
|
if (TON_ARCH AND NOT MSVC)
|
||||||
|
CHECK_CXX_COMPILER_FLAG( "-march=${TON_ARCH}" COMPILER_OPT_ARCH_SUPPORTED )
|
||||||
if (TON_ARCH STREQUAL "apple-m1")
|
if (TON_ARCH STREQUAL "apple-m1")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=${TON_ARCH}")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=${TON_ARCH}")
|
||||||
else()
|
elseif(COMPILER_OPT_ARCH_SUPPORTED)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${TON_ARCH}")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${TON_ARCH}")
|
||||||
|
elseif(NOT TON_ARCH STREQUAL "native")
|
||||||
|
message(FATAL_ERROR "Compiler doesn't support arch ${TON_ARCH}")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if (THREADS_HAVE_PTHREAD_ARG)
|
if (THREADS_HAVE_PTHREAD_ARG)
|
||||||
|
@ -242,12 +249,16 @@ elseif (CLANG OR GCC)
|
||||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -fvisibility=hidden -Wl,-dead_strip,-x,-S")
|
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -fvisibility=hidden -Wl,-dead_strip,-x,-S")
|
||||||
else()
|
else()
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections")
|
||||||
|
if (NOT USE_EMSCRIPTEN)
|
||||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,--exclude-libs,ALL")
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,--exclude-libs,ALL")
|
||||||
|
endif()
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
|
||||||
if (NOT TON_USE_ASAN AND NOT TON_USE_TSAN AND NOT MEMPROF)
|
if (NOT TON_USE_ASAN AND NOT TON_USE_TSAN AND NOT MEMPROF)
|
||||||
|
if (NOT USE_EMSCRIPTEN)
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--exclude-libs,ALL")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--exclude-libs,ALL")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
|
16
README.md
16
README.md
|
@ -1,3 +1,19 @@
|
||||||
|
[![Stack Overflow Group][stack-overflow-badge]][stack-overflow-url]
|
||||||
|
[![Telegram Foundation Group][telegram-foundation-badge]][telegram-foundation-url]
|
||||||
|
[![Telegram Community Group][telegram-community-badge]][telegram-community-url]
|
||||||
|
[![Twitter Group][twitter-badge]][twitter-url]
|
||||||
|
|
||||||
|
[telegram-foundation-badge]: https://img.shields.io/badge/-TON%20Foundation-2CA5E0?style=flat&logo=telegram&logoColor=white
|
||||||
|
[telegram-community-badge]: https://img.shields.io/badge/-TON%20Community-2CA5E0?style=flat&logo=telegram&logoColor=white
|
||||||
|
[telegram-foundation-url]: https://t.me/tonblockchain
|
||||||
|
[telegram-community-url]: https://t.me/toncoin
|
||||||
|
[twitter-badge]: https://img.shields.io/twitter/follow/ton_blockchain
|
||||||
|
[twitter-url]: https://twitter.com/ton_blockchain
|
||||||
|
[stack-overflow-badge]: https://img.shields.io/badge/-Stack%20Overflow-FE7A16?style=flat&logo=stack-overflow&logoColor=white
|
||||||
|
[stack-overflow-url]: https://stackoverflow.com/questions/tagged/ton
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# TON
|
# TON
|
||||||
|
|
||||||
Main TON monorepo, which includes the code of the node/validator, lite-client, tonlib, FunC compiler, etc.
|
Main TON monorepo, which includes the code of the node/validator, lite-client, tonlib, FunC compiler, etc.
|
||||||
|
|
|
@ -188,7 +188,6 @@ void Receiver::receive_from_client(td::IPAddress addr, td::BufferSlice data) {
|
||||||
p.data = std::move(data);
|
p.data = std::move(data);
|
||||||
p.adnl_start_time = start_time();
|
p.adnl_start_time = start_time();
|
||||||
p.seqno = out_seqno_;
|
p.seqno = out_seqno_;
|
||||||
p.data = std::move(data);
|
|
||||||
|
|
||||||
auto enc = proxy_->encrypt(std::move(p));
|
auto enc = proxy_->encrypt(std::move(p));
|
||||||
|
|
||||||
|
|
|
@ -179,6 +179,7 @@ set(FUNC_LIB_SOURCE
|
||||||
func/stack-transform.cpp
|
func/stack-transform.cpp
|
||||||
func/optimize.cpp
|
func/optimize.cpp
|
||||||
func/codegen.cpp
|
func/codegen.cpp
|
||||||
|
func/func.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(TLB_BLOCK_AUTO
|
set(TLB_BLOCK_AUTO
|
||||||
|
@ -266,6 +267,8 @@ set(BIGINT_TEST_SOURCE
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(USE_EMSCRIPTEN ${USE_EMSCRIPTEN} PARENT_SCOPE)
|
||||||
|
|
||||||
|
|
||||||
add_library(ton_crypto STATIC ${TON_CRYPTO_SOURCE})
|
add_library(ton_crypto STATIC ${TON_CRYPTO_SOURCE})
|
||||||
target_include_directories(ton_crypto PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
target_include_directories(ton_crypto PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||||
|
@ -305,13 +308,30 @@ target_include_directories(ton_block PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SO
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/block> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/block> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
|
||||||
target_link_libraries(ton_block PUBLIC ton_crypto tdutils tdactor tl_api)
|
target_link_libraries(ton_block PUBLIC ton_crypto tdutils tdactor tl_api)
|
||||||
|
|
||||||
add_executable(func func/func.cpp ${FUNC_LIB_SOURCE})
|
add_executable(func func/func-main.cpp ${FUNC_LIB_SOURCE})
|
||||||
target_include_directories(func PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
target_include_directories(func PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
||||||
target_link_libraries(func PUBLIC ton_crypto src_parser git ton_block)
|
target_link_libraries(func PUBLIC ton_crypto src_parser git ton_block)
|
||||||
if (WINGETOPT_FOUND)
|
if (WINGETOPT_FOUND)
|
||||||
target_link_libraries_system(func wingetopt)
|
target_link_libraries_system(func wingetopt)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (USE_EMSCRIPTEN)
|
||||||
|
add_executable(funcfiftlib funcfiftlib/funcfiftlib.cpp ${FUNC_LIB_SOURCE})
|
||||||
|
target_include_directories(funcfiftlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
||||||
|
target_link_libraries(funcfiftlib PUBLIC fift-lib src_parser git)
|
||||||
|
target_link_options(funcfiftlib PRIVATE -sEXPORTED_RUNTIME_METHODS=FS,ccall,cwrap,_malloc,free,UTF8ToString,stringToUTF8)
|
||||||
|
target_link_options(funcfiftlib PRIVATE -sEXPORTED_FUNCTIONS=_func_compile,_version)
|
||||||
|
target_link_options(funcfiftlib PRIVATE -sEXPORT_NAME=CompilerModule)
|
||||||
|
target_link_options(funcfiftlib PRIVATE -sERROR_ON_UNDEFINED_SYMBOLS=0)
|
||||||
|
target_link_options(funcfiftlib PRIVATE -sFILESYSTEM=1)
|
||||||
|
target_link_options(funcfiftlib PRIVATE -Oz)
|
||||||
|
target_link_options(funcfiftlib PRIVATE -sIGNORE_MISSING_MAIN=1)
|
||||||
|
target_link_options(funcfiftlib PRIVATE -sAUTO_NATIVE_LIBRARIES=0)
|
||||||
|
target_link_options(funcfiftlib PRIVATE -sMODULARIZE=1)
|
||||||
|
target_link_options(funcfiftlib PRIVATE --embed-file ${CMAKE_CURRENT_SOURCE_DIR}/fift/lib@/fiftlib)
|
||||||
|
target_compile_options(funcfiftlib PRIVATE -sDISABLE_EXCEPTION_CATCHING=0)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(tlbc tl/tlbc.cpp)
|
add_executable(tlbc tl/tlbc.cpp)
|
||||||
target_include_directories(tlbc PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
target_include_directories(tlbc PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
||||||
target_link_libraries(tlbc PUBLIC ton_crypto src_parser)
|
target_link_libraries(tlbc PUBLIC ton_crypto src_parser)
|
||||||
|
@ -337,8 +357,9 @@ if (TON_USE_ASAN AND NOT WIN32)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
file(MAKE_DIRECTORY smartcont/auto)
|
file(MAKE_DIRECTORY smartcont/auto)
|
||||||
if (NOT CMAKE_CROSSCOMPILING)
|
if (NOT CMAKE_CROSSCOMPILING OR USE_EMSCRIPTEN)
|
||||||
set(GENERATE_TLB_CMD tlbc)
|
set(GENERATE_TLB_CMD tlbc)
|
||||||
|
if (NOT USE_EMSCRIPTEN)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/block
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/block
|
||||||
COMMAND ${TURN_OFF_LSAN}
|
COMMAND ${TURN_OFF_LSAN}
|
||||||
|
@ -347,6 +368,15 @@ if (NOT CMAKE_CROSSCOMPILING)
|
||||||
OUTPUT ${TLB_BLOCK_AUTO}
|
OUTPUT ${TLB_BLOCK_AUTO}
|
||||||
DEPENDS tlbc block/block.tlb
|
DEPENDS tlbc block/block.tlb
|
||||||
)
|
)
|
||||||
|
else()
|
||||||
|
add_custom_command(
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/block
|
||||||
|
COMMAND ${TURN_OFF_LSAN}
|
||||||
|
COMMENT "Generate block tlb source files"
|
||||||
|
OUTPUT ${TLB_BLOCK_AUTO}
|
||||||
|
DEPENDS tlbc block/block.tlb
|
||||||
|
)
|
||||||
|
endif()
|
||||||
add_custom_target(tlb_generate_block DEPENDS ${TLB_BLOCK_AUTO})
|
add_custom_target(tlb_generate_block DEPENDS ${TLB_BLOCK_AUTO})
|
||||||
add_dependencies(ton_block tlb_generate_block)
|
add_dependencies(ton_block tlb_generate_block)
|
||||||
|
|
||||||
|
|
|
@ -982,7 +982,7 @@ struct ShardIdent::Record {
|
||||||
int shard_pfx_bits;
|
int shard_pfx_bits;
|
||||||
int workchain_id;
|
int workchain_id;
|
||||||
unsigned long long shard_prefix;
|
unsigned long long shard_prefix;
|
||||||
Record() : shard_pfx_bits(-1) {
|
Record() : shard_pfx_bits(-1), workchain_id(ton::workchainInvalid), shard_prefix(0) {
|
||||||
}
|
}
|
||||||
Record(int _pfxlen, int _wcid, unsigned long long _pfx)
|
Record(int _pfxlen, int _wcid, unsigned long long _pfx)
|
||||||
: shard_pfx_bits(_pfxlen), workchain_id(_wcid), shard_prefix(_pfx) {
|
: shard_pfx_bits(_pfxlen), workchain_id(_wcid), shard_prefix(_pfx) {
|
||||||
|
|
|
@ -819,7 +819,7 @@ vmc_pushint$1111 value:int32 next:^VmCont = VmCont;
|
||||||
//
|
//
|
||||||
// DNS RECORDS
|
// DNS RECORDS
|
||||||
//
|
//
|
||||||
_ (HashmapE 256 DNSRecord) = DNS_RecordSet;
|
_ (HashmapE 256 ^DNSRecord) = DNS_RecordSet;
|
||||||
|
|
||||||
chunk_ref$_ {n:#} ref:^(TextChunks (n + 1)) = TextChunkRef (n + 1);
|
chunk_ref$_ {n:#} ref:^(TextChunks (n + 1)) = TextChunkRef (n + 1);
|
||||||
chunk_ref_empty$_ = TextChunkRef 0;
|
chunk_ref_empty$_ = TextChunkRef 0;
|
||||||
|
|
|
@ -327,8 +327,7 @@ bool Account::unpack(Ref<vm::CellSlice> shard_account, Ref<vm::CellSlice> extra,
|
||||||
block::gen::t_ShardAccount.print(std::cerr, *shard_account);
|
block::gen::t_ShardAccount.print(std::cerr, *shard_account);
|
||||||
}
|
}
|
||||||
block::gen::ShardAccount::Record acc_info;
|
block::gen::ShardAccount::Record acc_info;
|
||||||
if (!(block::gen::t_ShardAccount.validate_csr(shard_account) &&
|
if (!(block::tlb::t_ShardAccount.validate_csr(shard_account) && tlb::unpack_exact(shard_account.write(), acc_info))) {
|
||||||
block::tlb::t_ShardAccount.validate_csr(shard_account) && tlb::unpack_exact(shard_account.write(), acc_info))) {
|
|
||||||
LOG(ERROR) << "account " << addr.to_hex() << " state is invalid";
|
LOG(ERROR) << "account " << addr.to_hex() << " state is invalid";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2013,7 +2012,6 @@ bool Transaction::compute_state() {
|
||||||
std::cerr << "new account state: ";
|
std::cerr << "new account state: ";
|
||||||
block::gen::t_Account.print_ref(std::cerr, new_total_state);
|
block::gen::t_Account.print_ref(std::cerr, new_total_state);
|
||||||
}
|
}
|
||||||
CHECK(block::gen::t_Account.validate_ref(new_total_state));
|
|
||||||
CHECK(block::tlb::t_Account.validate_ref(new_total_state));
|
CHECK(block::tlb::t_Account.validate_ref(new_total_state));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,6 +176,7 @@ class AnyIntView {
|
||||||
public:
|
public:
|
||||||
enum { word_bits = Tr::word_bits, word_shift = Tr::word_shift };
|
enum { word_bits = Tr::word_bits, word_shift = Tr::word_shift };
|
||||||
typedef typename Tr::word_t word_t;
|
typedef typename Tr::word_t word_t;
|
||||||
|
typedef typename Tr::uword_t uword_t;
|
||||||
int& n_;
|
int& n_;
|
||||||
PropagateConstSpan<word_t> digits;
|
PropagateConstSpan<word_t> digits;
|
||||||
|
|
||||||
|
@ -320,7 +321,7 @@ class BigIntG {
|
||||||
digits[0] = x;
|
digits[0] = x;
|
||||||
normalize_bool();
|
normalize_bool();
|
||||||
} else {
|
} else {
|
||||||
digits[0] = ((x + Tr::Half) & (Tr::Base - 1)) - Tr::Half;
|
digits[0] = ((x ^ Tr::Half) & (Tr::Base - 1)) - Tr::Half;
|
||||||
digits[n++] = (x >> Tr::word_shift) + (digits[0] < 0);
|
digits[n++] = (x >> Tr::word_shift) + (digits[0] < 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -675,7 +676,7 @@ class BigIntG {
|
||||||
return n > 0 && !(digits[0] & 1);
|
return n > 0 && !(digits[0] & 1);
|
||||||
}
|
}
|
||||||
word_t mod_pow2_short(int pow) const {
|
word_t mod_pow2_short(int pow) const {
|
||||||
return n > 0 ? digits[0] & ((1 << pow) - 1) : 0;
|
return n > 0 ? digits[0] & ((1ULL << pow) - 1) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -764,7 +765,7 @@ bool AnyIntView<Tr>::add_pow2_any(int exponent, int factor) {
|
||||||
while (size() <= k) {
|
while (size() <= k) {
|
||||||
digits[inc_size()] = 0;
|
digits[inc_size()] = 0;
|
||||||
}
|
}
|
||||||
digits[k] += ((word_t)factor << dm.rem);
|
digits[k] += factor * ((word_t)1 << dm.rem);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -969,7 +970,7 @@ bool AnyIntView<Tr>::add_mul_any(const AnyIntView<Tr>& yp, const AnyIntView<Tr>&
|
||||||
if (hi && hi != -1) {
|
if (hi && hi != -1) {
|
||||||
return invalidate_bool();
|
return invalidate_bool();
|
||||||
}
|
}
|
||||||
digits[size() - 1] += (hi << word_shift);
|
digits[size() - 1] += ((uword_t)hi << word_shift);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1014,7 +1015,7 @@ int AnyIntView<Tr>::sgn_un_any() const {
|
||||||
}
|
}
|
||||||
int i = size() - 2;
|
int i = size() - 2;
|
||||||
do {
|
do {
|
||||||
v <<= word_shift;
|
v *= Tr::Base;
|
||||||
word_t w = digits[i];
|
word_t w = digits[i];
|
||||||
if (w >= -v + Tr::MaxDenorm) {
|
if (w >= -v + Tr::MaxDenorm) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1059,7 +1060,7 @@ typename Tr::word_t AnyIntView<Tr>::to_long_any() const {
|
||||||
} else if (size() == 1) {
|
} else if (size() == 1) {
|
||||||
return digits[0];
|
return digits[0];
|
||||||
} else {
|
} else {
|
||||||
word_t v = digits[0] + (digits[1] << word_shift); // approximation mod 2^64
|
word_t v = (uword_t)digits[0] + ((uword_t)digits[1] << word_shift); // approximation mod 2^64
|
||||||
word_t w = (v & (Tr::Base - 1)) - digits[0];
|
word_t w = (v & (Tr::Base - 1)) - digits[0];
|
||||||
w >>= word_shift;
|
w >>= word_shift;
|
||||||
w += (v >> word_shift); // excess of approximation divided by Tr::Base
|
w += (v >> word_shift); // excess of approximation divided by Tr::Base
|
||||||
|
@ -1120,7 +1121,7 @@ int AnyIntView<Tr>::cmp_un_any(const AnyIntView<Tr>& yp) const {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
while (xn > yn) {
|
while (xn > yn) {
|
||||||
v <<= word_shift;
|
v *= Tr::Base;
|
||||||
word_t w = T::eval(digits[--xn]);
|
word_t w = T::eval(digits[--xn]);
|
||||||
if (w >= -v + Tr::MaxDenorm) {
|
if (w >= -v + Tr::MaxDenorm) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1137,7 +1138,7 @@ int AnyIntView<Tr>::cmp_un_any(const AnyIntView<Tr>& yp) const {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
while (yn > xn) {
|
while (yn > xn) {
|
||||||
v <<= word_shift;
|
v *= Tr::Base;
|
||||||
word_t w = yp.digits[--yn];
|
word_t w = yp.digits[--yn];
|
||||||
if (w <= v - Tr::MaxDenorm) {
|
if (w <= v - Tr::MaxDenorm) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1150,7 +1151,7 @@ int AnyIntView<Tr>::cmp_un_any(const AnyIntView<Tr>& yp) const {
|
||||||
v = 0;
|
v = 0;
|
||||||
}
|
}
|
||||||
while (--xn >= 0) {
|
while (--xn >= 0) {
|
||||||
v <<= word_shift;
|
v *= Tr::Base;
|
||||||
word_t w = T::eval(digits[xn]) - yp.digits[xn];
|
word_t w = T::eval(digits[xn]) - yp.digits[xn];
|
||||||
if (w >= -v + Tr::MaxDenorm) {
|
if (w >= -v + Tr::MaxDenorm) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1197,7 +1198,7 @@ int AnyIntView<Tr>::divmod_tiny_any(int y) {
|
||||||
}
|
}
|
||||||
int rem = 0;
|
int rem = 0;
|
||||||
for (int i = size() - 1; i >= 0; i--) {
|
for (int i = size() - 1; i >= 0; i--) {
|
||||||
auto divmod = std::div(digits[i] + ((word_t)rem << word_shift), (word_t)y);
|
auto divmod = std::div(digits[i] + ((uword_t)rem << word_shift), (word_t)y);
|
||||||
digits[i] = divmod.quot;
|
digits[i] = divmod.quot;
|
||||||
rem = (int)divmod.rem;
|
rem = (int)divmod.rem;
|
||||||
if ((rem ^ y) < 0 && rem) {
|
if ((rem ^ y) < 0 && rem) {
|
||||||
|
@ -1267,7 +1268,7 @@ bool AnyIntView<Tr>::mul_add_short_any(word_t y, word_t z) {
|
||||||
z += (digits[size() - 1] >> word_shift);
|
z += (digits[size() - 1] >> word_shift);
|
||||||
digits[size() - 1] &= Tr::Base - 1;
|
digits[size() - 1] &= Tr::Base - 1;
|
||||||
if (!z || z == -1) {
|
if (!z || z == -1) {
|
||||||
digits[size() - 1] += (z << word_shift);
|
digits[size() - 1] += ((uword_t)z << word_shift);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1338,7 +1339,7 @@ bool AnyIntView<Tr>::mod_div_any(const AnyIntView<Tr>& yp, AnyIntView<Tr>& quot,
|
||||||
while (--i >= 0) {
|
while (--i >= 0) {
|
||||||
Tr::sub_mul(&digits[k + i + 1], &digits[k + i], q, yp.digits[i]);
|
Tr::sub_mul(&digits[k + i + 1], &digits[k + i], q, yp.digits[i]);
|
||||||
}
|
}
|
||||||
digits[size() - 1] += (hi << word_shift);
|
digits[size() - 1] += ((uword_t)hi << word_shift);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
quot.set_size(1);
|
quot.set_size(1);
|
||||||
|
@ -1351,7 +1352,7 @@ bool AnyIntView<Tr>::mod_div_any(const AnyIntView<Tr>& yp, AnyIntView<Tr>& quot,
|
||||||
Tr::sub_mul(&digits[k + i + 1], &digits[k + i], q, yp.digits[i]);
|
Tr::sub_mul(&digits[k + i + 1], &digits[k + i], q, yp.digits[i]);
|
||||||
}
|
}
|
||||||
dec_size();
|
dec_size();
|
||||||
digits[size() - 1] += (digits[size()] << word_shift);
|
digits[size() - 1] += ((uword_t)digits[size()] << word_shift);
|
||||||
}
|
}
|
||||||
if (size() >= yp.size() - 1) {
|
if (size() >= yp.size() - 1) {
|
||||||
assert(size() <= yp.size());
|
assert(size() <= yp.size());
|
||||||
|
@ -1455,7 +1456,7 @@ bool AnyIntView<Tr>::mod_pow2_any(int exponent) {
|
||||||
dec_size();
|
dec_size();
|
||||||
q += word_shift;
|
q += word_shift;
|
||||||
}
|
}
|
||||||
word_t pow = ((word_t)1 << q);
|
uword_t pow = ((uword_t)1 << q);
|
||||||
word_t v = digits[size() - 1] & (pow - 1);
|
word_t v = digits[size() - 1] & (pow - 1);
|
||||||
if (!v) {
|
if (!v) {
|
||||||
int k = size() - 1;
|
int k = size() - 1;
|
||||||
|
@ -1485,7 +1486,7 @@ bool AnyIntView<Tr>::mod_pow2_any(int exponent) {
|
||||||
return true;
|
return true;
|
||||||
} else if (v >= Tr::Half && size() < max_size()) {
|
} else if (v >= Tr::Half && size() < max_size()) {
|
||||||
word_t w = (((v >> (word_shift - 1)) + 1) >> 1);
|
word_t w = (((v >> (word_shift - 1)) + 1) >> 1);
|
||||||
digits[size() - 1] = v - (w << word_shift);
|
digits[size() - 1] = (uword_t)v - ((uword_t)w << word_shift);
|
||||||
digits[inc_size()] = w;
|
digits[inc_size()] = w;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1623,7 +1624,7 @@ bool AnyIntView<Tr>::lshift_any(int exponent) {
|
||||||
} else if (v != -1) {
|
} else if (v != -1) {
|
||||||
return invalidate_bool();
|
return invalidate_bool();
|
||||||
} else {
|
} else {
|
||||||
digits[size() - 1] += (v << word_shift);
|
digits[size() - 1] += ((uword_t)v << word_shift);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (q) {
|
if (q) {
|
||||||
|
@ -1750,7 +1751,7 @@ int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
|
||||||
int k = size() - 1;
|
int k = size() - 1;
|
||||||
word_t q = digits[k];
|
word_t q = digits[k];
|
||||||
if (k > 0 && q < Tr::MaxDenorm / 2) {
|
if (k > 0 && q < Tr::MaxDenorm / 2) {
|
||||||
q <<= word_shift;
|
q *= Tr::Base;
|
||||||
q += digits[--k];
|
q += digits[--k];
|
||||||
}
|
}
|
||||||
if (!k) {
|
if (!k) {
|
||||||
|
@ -1766,7 +1767,7 @@ int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
|
||||||
} else if (q <= -Tr::MaxDenorm / 2) {
|
} else if (q <= -Tr::MaxDenorm / 2) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
q <<= word_shift;
|
q *= Tr::Base;
|
||||||
q += digits[--k];
|
q += digits[--k];
|
||||||
}
|
}
|
||||||
return q >= 0 ? s + 1 : s;
|
return q >= 0 ? s + 1 : s;
|
||||||
|
@ -1774,7 +1775,7 @@ int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
|
||||||
int k = size() - 1;
|
int k = size() - 1;
|
||||||
word_t q = digits[k];
|
word_t q = digits[k];
|
||||||
if (k > 0 && q > -Tr::MaxDenorm / 2) {
|
if (k > 0 && q > -Tr::MaxDenorm / 2) {
|
||||||
q <<= word_shift;
|
q *= Tr::Base;
|
||||||
q += digits[--k];
|
q += digits[--k];
|
||||||
}
|
}
|
||||||
if (!k) {
|
if (!k) {
|
||||||
|
@ -1790,7 +1791,7 @@ int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
|
||||||
} else if (q <= -Tr::MaxDenorm / 2) {
|
} else if (q <= -Tr::MaxDenorm / 2) {
|
||||||
return s + 1;
|
return s + 1;
|
||||||
}
|
}
|
||||||
q <<= word_shift;
|
q *= Tr::Base;
|
||||||
q += digits[--k];
|
q += digits[--k];
|
||||||
}
|
}
|
||||||
return q >= 0 ? s : s + 1;
|
return q >= 0 ? s : s + 1;
|
||||||
|
@ -1817,7 +1818,7 @@ bool AnyIntView<Tr>::export_bytes_any(unsigned char* buff, std::size_t buff_size
|
||||||
for (int i = 0; i < size(); i++) {
|
for (int i = 0; i < size(); i++) {
|
||||||
if ((word_shift & 7) && word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
|
if ((word_shift & 7) && word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
|
||||||
int k1 = 8 - k;
|
int k1 = 8 - k;
|
||||||
v += (digits[i] << k) & 0xff;
|
v += ((uword_t)digits[i] << k) & 0xff;
|
||||||
if (ptr > buff) {
|
if (ptr > buff) {
|
||||||
*--ptr = (unsigned char)(v & 0xff);
|
*--ptr = (unsigned char)(v & 0xff);
|
||||||
} else if ((unsigned char)(v & 0xff) != s) {
|
} else if ((unsigned char)(v & 0xff) != s) {
|
||||||
|
@ -1827,7 +1828,7 @@ bool AnyIntView<Tr>::export_bytes_any(unsigned char* buff, std::size_t buff_size
|
||||||
v += (digits[i] >> k1);
|
v += (digits[i] >> k1);
|
||||||
k += word_shift - 8;
|
k += word_shift - 8;
|
||||||
} else {
|
} else {
|
||||||
v += (digits[i] << k);
|
v += ((uword_t)digits[i] << k);
|
||||||
k += word_shift;
|
k += word_shift;
|
||||||
}
|
}
|
||||||
while (k >= 8) {
|
while (k >= 8) {
|
||||||
|
@ -1868,7 +1869,7 @@ bool AnyIntView<Tr>::export_bytes_lsb_any(unsigned char* buff, std::size_t buff_
|
||||||
for (int i = 0; i < size(); i++) {
|
for (int i = 0; i < size(); i++) {
|
||||||
if ((word_shift & 7) && word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
|
if ((word_shift & 7) && word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
|
||||||
int k1 = 8 - k;
|
int k1 = 8 - k;
|
||||||
v += (digits[i] << k) & 0xff;
|
v += ((uword_t)digits[i] << k) & 0xff;
|
||||||
if (buff < end) {
|
if (buff < end) {
|
||||||
*buff++ = (unsigned char)(v & 0xff);
|
*buff++ = (unsigned char)(v & 0xff);
|
||||||
} else if ((unsigned char)(v & 0xff) != s) {
|
} else if ((unsigned char)(v & 0xff) != s) {
|
||||||
|
@ -1878,7 +1879,7 @@ bool AnyIntView<Tr>::export_bytes_lsb_any(unsigned char* buff, std::size_t buff_
|
||||||
v += (digits[i] >> k1);
|
v += (digits[i] >> k1);
|
||||||
k += word_shift - 8;
|
k += word_shift - 8;
|
||||||
} else {
|
} else {
|
||||||
v += (digits[i] << k);
|
v += ((uword_t)digits[i] << k);
|
||||||
k += word_shift;
|
k += word_shift;
|
||||||
}
|
}
|
||||||
while (k >= 8) {
|
while (k >= 8) {
|
||||||
|
@ -1922,7 +1923,7 @@ bool AnyIntView<Tr>::export_bits_any(unsigned char* buff, int offs, unsigned bit
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
td::bitstring::bits_store_long_top(buff, offs, v << (64 - bits), bits);
|
td::bitstring::bits_store_long_top(buff, offs, (unsigned long long)v << (64 - bits), bits);
|
||||||
} else {
|
} else {
|
||||||
if (!sgnd && v < 0) {
|
if (!sgnd && v < 0) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1945,7 +1946,7 @@ bool AnyIntView<Tr>::export_bits_any(unsigned char* buff, int offs, unsigned bit
|
||||||
for (int i = 0; i < size(); i++) {
|
for (int i = 0; i < size(); i++) {
|
||||||
if (word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
|
if (word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
|
||||||
int k1 = 8 - k;
|
int k1 = 8 - k;
|
||||||
v += (digits[i] << k) & 0xff;
|
v += ((uword_t)digits[i] << k) & 0xff;
|
||||||
if (ptr > buff) {
|
if (ptr > buff) {
|
||||||
if (--ptr > buff) {
|
if (--ptr > buff) {
|
||||||
*ptr = (unsigned char)(v & 0xff);
|
*ptr = (unsigned char)(v & 0xff);
|
||||||
|
@ -1963,7 +1964,7 @@ bool AnyIntView<Tr>::export_bits_any(unsigned char* buff, int offs, unsigned bit
|
||||||
v += (digits[i] >> k1);
|
v += (digits[i] >> k1);
|
||||||
k += word_shift - 8;
|
k += word_shift - 8;
|
||||||
} else {
|
} else {
|
||||||
v += (digits[i] << k);
|
v += ((uword_t)digits[i] << k);
|
||||||
k += word_shift;
|
k += word_shift;
|
||||||
}
|
}
|
||||||
while (k >= 8) {
|
while (k >= 8) {
|
||||||
|
@ -2028,7 +2029,7 @@ bool AnyIntView<Tr>::import_bytes_any(const unsigned char* buff, std::size_t buf
|
||||||
return invalidate_bool();
|
return invalidate_bool();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
v |= (((word_t) * --ptr) << k);
|
v |= (((uword_t) * --ptr) << k);
|
||||||
k += 8;
|
k += 8;
|
||||||
}
|
}
|
||||||
if (s) {
|
if (s) {
|
||||||
|
@ -2043,7 +2044,9 @@ bool AnyIntView<Tr>::import_bits_any(const unsigned char* buff, int offs, unsign
|
||||||
if (bits < word_shift) {
|
if (bits < word_shift) {
|
||||||
set_size(1);
|
set_size(1);
|
||||||
unsigned long long val = td::bitstring::bits_load_long_top(buff, offs, bits);
|
unsigned long long val = td::bitstring::bits_load_long_top(buff, offs, bits);
|
||||||
if (sgnd) {
|
if (bits == 0) {
|
||||||
|
digits[0] = 0;
|
||||||
|
} else if (sgnd) {
|
||||||
digits[0] = ((long long)val >> (64 - bits));
|
digits[0] = ((long long)val >> (64 - bits));
|
||||||
} else {
|
} else {
|
||||||
digits[0] = (val >> (64 - bits));
|
digits[0] = (val >> (64 - bits));
|
||||||
|
|
|
@ -191,7 +191,7 @@ void bits_memcpy(unsigned char* to, int to_offs, const unsigned char* from, int
|
||||||
*to++ = (unsigned char)(acc >> b);
|
*to++ = (unsigned char)(acc >> b);
|
||||||
}
|
}
|
||||||
if (b > 0) {
|
if (b > 0) {
|
||||||
*to = (unsigned char)((*to & (0xff >> b)) | ((int)acc << (8 - b)));
|
*to = (unsigned char)((*to & (0xff >> b)) | ((unsigned)acc << (8 - b)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,7 +301,7 @@ std::size_t bits_memscan(const unsigned char* ptr, int offs, std::size_t bit_cou
|
||||||
ptr++;
|
ptr++;
|
||||||
}
|
}
|
||||||
while (rem >= 8 && !td::is_aligned_pointer<8>(ptr)) {
|
while (rem >= 8 && !td::is_aligned_pointer<8>(ptr)) {
|
||||||
v = ((*ptr++ ^ xor_val) << 24);
|
v = ((unsigned)(*ptr++ ^ xor_val) << 24);
|
||||||
// std::cerr << "[B] rem=" << rem << " ptr=" << (const void*)(ptr - 1) << " v=" << std::hex << v << std::dec << std::endl;
|
// std::cerr << "[B] rem=" << rem << " ptr=" << (const void*)(ptr - 1) << " v=" << std::hex << v << std::dec << std::endl;
|
||||||
if (v) {
|
if (v) {
|
||||||
return bit_count - rem + td::count_leading_zeroes_non_zero32(v);
|
return bit_count - rem + td::count_leading_zeroes_non_zero32(v);
|
||||||
|
@ -319,7 +319,7 @@ std::size_t bits_memscan(const unsigned char* ptr, int offs, std::size_t bit_cou
|
||||||
rem -= 64;
|
rem -= 64;
|
||||||
}
|
}
|
||||||
while (rem >= 8) {
|
while (rem >= 8) {
|
||||||
v = ((*ptr++ ^ xor_val) << 24);
|
v = ((unsigned)(*ptr++ ^ xor_val) << 24);
|
||||||
// std::cerr << "[D] rem=" << rem << " ptr=" << (const void*)(ptr - 1) << " v=" << std::hex << v << std::dec << std::endl;
|
// std::cerr << "[D] rem=" << rem << " ptr=" << (const void*)(ptr - 1) << " v=" << std::hex << v << std::dec << std::endl;
|
||||||
if (v) {
|
if (v) {
|
||||||
return bit_count - rem + td::count_leading_zeroes_non_zero32(v);
|
return bit_count - rem + td::count_leading_zeroes_non_zero32(v);
|
||||||
|
@ -327,7 +327,7 @@ std::size_t bits_memscan(const unsigned char* ptr, int offs, std::size_t bit_cou
|
||||||
rem -= 8;
|
rem -= 8;
|
||||||
}
|
}
|
||||||
if (rem > 0) {
|
if (rem > 0) {
|
||||||
v = ((*ptr ^ xor_val) << 24);
|
v = ((unsigned)(*ptr ^ xor_val) << 24);
|
||||||
// std::cerr << "[E] rem=" << rem << " ptr=" << (const void*)ptr << " v=" << std::hex << v << std::dec << std::endl;
|
// std::cerr << "[E] rem=" << rem << " ptr=" << (const void*)ptr << " v=" << std::hex << v << std::dec << std::endl;
|
||||||
c = td::count_leading_zeroes32(v);
|
c = td::count_leading_zeroes32(v);
|
||||||
return c < rem ? bit_count - rem + c : bit_count;
|
return c < rem ? bit_count - rem + c : bit_count;
|
||||||
|
@ -505,7 +505,7 @@ unsigned long long bits_load_long_top(ConstBitPtr from, unsigned top_bits) {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long long bits_load_ulong(ConstBitPtr from, unsigned bits) {
|
unsigned long long bits_load_ulong(ConstBitPtr from, unsigned bits) {
|
||||||
return bits_load_long_top(from, bits) >> (64 - bits);
|
return bits == 0 ? 0 : bits_load_long_top(from, bits) >> (64 - bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
long long bits_load_long(ConstBitPtr from, unsigned bits) {
|
long long bits_load_long(ConstBitPtr from, unsigned bits) {
|
||||||
|
|
91
crypto/func/auto-tests/run_tests.py
Normal file
91
crypto/func/auto-tests/run_tests.py
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
def getenv(name, default=None):
|
||||||
|
if name in os.environ:
|
||||||
|
return os.environ[name]
|
||||||
|
if default is None:
|
||||||
|
print("Environment variable", name, "is not set", file=sys.stderr)
|
||||||
|
exit(1)
|
||||||
|
return default
|
||||||
|
|
||||||
|
FUNC_EXECUTABLE = getenv("FUNC_EXECUTABLE", "func")
|
||||||
|
FIFT_EXECUTABLE = getenv("FIFT_EXECUTABLE", "fift")
|
||||||
|
#FUNC_STDLIB = getenv("FUNC_STDLIB")
|
||||||
|
FIFT_LIBS = getenv("FIFT_LIBS")
|
||||||
|
TMP_DIR = tempfile.mkdtemp()
|
||||||
|
COMPILED_FIF = os.path.join(TMP_DIR, "compiled.fif")
|
||||||
|
RUNNER_FIF = os.path.join(TMP_DIR, "runner.fif")
|
||||||
|
|
||||||
|
if len(sys.argv) != 2:
|
||||||
|
print("Usage : run_tests.py tests_dir", file=sys.stderr)
|
||||||
|
exit(1)
|
||||||
|
TESTS_DIR = sys.argv[1]
|
||||||
|
|
||||||
|
class ExecutionError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def compile_func(f):
|
||||||
|
res = subprocess.run([FUNC_EXECUTABLE, "-o", COMPILED_FIF, "-SPA", f], capture_output=True, timeout=10)
|
||||||
|
if res.returncode != 0:
|
||||||
|
raise ExecutionError(str(res.stderr, "utf-8"))
|
||||||
|
|
||||||
|
def run_runner():
|
||||||
|
res = subprocess.run([FIFT_EXECUTABLE, "-I", FIFT_LIBS, RUNNER_FIF], capture_output=True, timeout=10)
|
||||||
|
if res.returncode != 0:
|
||||||
|
raise ExecutionError(str(res.stderr, "utf-8"))
|
||||||
|
s = str(res.stdout, "utf-8")
|
||||||
|
s = [x.strip() for x in s.split("\n")]
|
||||||
|
return [x for x in s if x != ""]
|
||||||
|
|
||||||
|
tests = [s for s in os.listdir(TESTS_DIR) if s.endswith(".fc")]
|
||||||
|
tests.sort()
|
||||||
|
print("Found", len(tests), "tests", file=sys.stderr)
|
||||||
|
for ti, tf in enumerate(tests):
|
||||||
|
print("Running test %d/%d: %s" % (ti + 1, len(tests), tf), file=sys.stderr)
|
||||||
|
tf = os.path.join(TESTS_DIR, tf)
|
||||||
|
try:
|
||||||
|
compile_func(tf)
|
||||||
|
except ExecutionError as e:
|
||||||
|
print(file=sys.stderr)
|
||||||
|
print("Compilation error", file=sys.stderr)
|
||||||
|
print(e, file=sys.stderr)
|
||||||
|
exit(2)
|
||||||
|
with open(tf, "r") as fd:
|
||||||
|
lines = fd.readlines()
|
||||||
|
cases = []
|
||||||
|
for s in lines:
|
||||||
|
s = [x.strip() for x in s.split("|")]
|
||||||
|
if len(s) == 4 and s[0].strip() == "TESTCASE":
|
||||||
|
cases.append(s[1:])
|
||||||
|
if len(cases) == 0:
|
||||||
|
print(file=sys.stderr)
|
||||||
|
print("Error: no test cases", file=sys.stderr)
|
||||||
|
exit(2)
|
||||||
|
|
||||||
|
with open(RUNNER_FIF, "w") as f:
|
||||||
|
print("\"%s\" include <s constant code" % COMPILED_FIF, file=f)
|
||||||
|
for function, test_in, _ in cases:
|
||||||
|
print(test_in, function, "code 1 runvmx abort\"exitcode is not 0\" .s cr { drop } depth 1- times", file=f)
|
||||||
|
try:
|
||||||
|
func_out = run_runner()
|
||||||
|
if len(func_out) != len(cases):
|
||||||
|
raise ExecutionError("Unexpected number of lines")
|
||||||
|
for i in range(len(func_out)):
|
||||||
|
if func_out[i] != cases[i][2]:
|
||||||
|
raise ExecutionError("Error on case %d: expected '%s', found '%s'" % (i + 1, cases[i][2], func_out[i]))
|
||||||
|
except ExecutionError as e:
|
||||||
|
print(file=sys.stderr)
|
||||||
|
print("Error:", file=sys.stderr)
|
||||||
|
print(e, file=sys.stderr)
|
||||||
|
print(file=sys.stderr)
|
||||||
|
print("Compiled:", file=sys.stderr)
|
||||||
|
with open(COMPILED_FIF, "r") as f:
|
||||||
|
print(f.read(), file=sys.stderr)
|
||||||
|
exit(2)
|
||||||
|
print(" OK, %d cases" % len(cases), file=sys.stderr)
|
||||||
|
|
||||||
|
print("Done", file=sys.stderr)
|
239
crypto/func/auto-tests/stress_tester.py
Normal file
239
crypto/func/auto-tests/stress_tester.py
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import random
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
def getenv(name, default=None):
|
||||||
|
if name in os.environ:
|
||||||
|
return os.environ[name]
|
||||||
|
if default is not None:
|
||||||
|
return default
|
||||||
|
print("Environemnt variable", name, "is not set", file=sys.stderr)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
VAR_CNT = 5
|
||||||
|
TMP_DIR = tempfile.mkdtemp()
|
||||||
|
FUNC_EXECUTABLE = getenv("FUNC_EXECUTABLE", "func")
|
||||||
|
FIFT_EXECUTABLE = getenv("FIFT_EXECUTABLE", "fift")
|
||||||
|
FIFT_LIBS = getenv("FIFT_LIBS")
|
||||||
|
MAGIC = 123456789
|
||||||
|
|
||||||
|
var_idx = 0
|
||||||
|
def gen_var_name():
|
||||||
|
global var_idx
|
||||||
|
var_idx += 1
|
||||||
|
return "i%d" % var_idx
|
||||||
|
|
||||||
|
class State:
|
||||||
|
def __init__(self, x):
|
||||||
|
self.x = x
|
||||||
|
self.vs = [0] * VAR_CNT
|
||||||
|
|
||||||
|
class Code:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class CodeEmpty(Code):
|
||||||
|
def execute(self, state):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def write(self, f, indent=0):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class CodeReturn(Code):
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def execute(self, state):
|
||||||
|
return [self.value] + state.vs
|
||||||
|
|
||||||
|
def write(self, f, indent=0):
|
||||||
|
print(" " * indent + "return (%d, %s);" % (self.value, ", ".join("v%d" % i for i in range(VAR_CNT))), file=f)
|
||||||
|
|
||||||
|
class CodeAdd(Code):
|
||||||
|
def __init__(self, i, value):
|
||||||
|
self.i = i
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def execute(self, state):
|
||||||
|
state.vs[self.i] += self.value
|
||||||
|
return None
|
||||||
|
|
||||||
|
def write(self, f, indent=0):
|
||||||
|
print(" " * indent + "v%d += %d;" % (self.i, self.value), file=f)
|
||||||
|
|
||||||
|
class CodeBlock(Code):
|
||||||
|
def __init__(self, code):
|
||||||
|
self.code = code
|
||||||
|
|
||||||
|
def execute(self, state):
|
||||||
|
for c in self.code:
|
||||||
|
res = c.execute(state)
|
||||||
|
if res is not None:
|
||||||
|
return res
|
||||||
|
return None
|
||||||
|
|
||||||
|
def write(self, f, indent=0):
|
||||||
|
for c in self.code:
|
||||||
|
c.write(f, indent)
|
||||||
|
|
||||||
|
class CodeIfRange(Code):
|
||||||
|
def __init__(self, l, r, c1, c2):
|
||||||
|
self.l = l
|
||||||
|
self.r = r
|
||||||
|
self.c1 = c1
|
||||||
|
self.c2 = c2
|
||||||
|
|
||||||
|
def execute(self, state):
|
||||||
|
if self.l <= state.x < self.r:
|
||||||
|
return self.c1.execute(state)
|
||||||
|
else:
|
||||||
|
return self.c2.execute(state)
|
||||||
|
|
||||||
|
def write(self, f, indent=0):
|
||||||
|
print(" " * indent + "if (in(x, %d, %d)) {" % (self.l, self.r), file=f)
|
||||||
|
self.c1.write(f, indent + 1)
|
||||||
|
if isinstance(self.c2, CodeEmpty):
|
||||||
|
print(" " * indent + "}", file=f)
|
||||||
|
else:
|
||||||
|
print(" " * indent + "} else {", file=f)
|
||||||
|
self.c2.write(f, indent + 1)
|
||||||
|
print(" " * indent + "}", file=f)
|
||||||
|
|
||||||
|
class CodeRepeat(Code):
|
||||||
|
def __init__(self, n, c, loop_type):
|
||||||
|
if loop_type == 2:
|
||||||
|
n = max(n, 1)
|
||||||
|
self.n = n
|
||||||
|
self.c = c
|
||||||
|
self.loop_type = loop_type
|
||||||
|
|
||||||
|
def execute(self, state):
|
||||||
|
for _ in range(self.n):
|
||||||
|
res = self.c.execute(state)
|
||||||
|
if res is not None:
|
||||||
|
return res
|
||||||
|
return None
|
||||||
|
|
||||||
|
def write(self, f, indent=0):
|
||||||
|
if self.loop_type == 0:
|
||||||
|
print(" " * indent + "repeat (%d) {" % self.n, file=f)
|
||||||
|
self.c.write(f, indent + 1)
|
||||||
|
print(" " * indent + "}", file=f)
|
||||||
|
elif self.loop_type == 1:
|
||||||
|
var = gen_var_name()
|
||||||
|
print(" " * indent + "int %s = 0;" % var, file=f)
|
||||||
|
print(" " * indent + "while (%s < %d) {" % (var, self.n), file=f)
|
||||||
|
self.c.write(f, indent + 1)
|
||||||
|
print(" " * (indent + 1) + "%s += 1;" % var, file=f)
|
||||||
|
print(" " * indent + "}", file=f)
|
||||||
|
else:
|
||||||
|
var = gen_var_name()
|
||||||
|
print(" " * indent + "int %s = 0;" % var, file=f)
|
||||||
|
print(" " * indent + "do {", file=f)
|
||||||
|
self.c.write(f, indent + 1)
|
||||||
|
print(" " * (indent + 1) + "%s += 1;" % var, file=f)
|
||||||
|
print(" " * indent + "} until (%s >= %d);" % (var, self.n), file=f)
|
||||||
|
|
||||||
|
def write_function(f, name, body, inline=False, inline_ref=False, method_id=None):
|
||||||
|
print("_ %s(int x)" % name, file=f, end="")
|
||||||
|
if inline:
|
||||||
|
print(" inline", file=f, end="")
|
||||||
|
if inline_ref:
|
||||||
|
print(" inline_ref", file=f, end="")
|
||||||
|
if method_id is not None:
|
||||||
|
print(" method_id(%d)" % method_id, file=f, end="")
|
||||||
|
print(" {", file=f)
|
||||||
|
for i in range(VAR_CNT):
|
||||||
|
print(" int v%d = 0;" % i, file=f)
|
||||||
|
body.write(f, 1);
|
||||||
|
print("}", file=f)
|
||||||
|
|
||||||
|
def gen_code(xl, xr, with_return, loop_depth=0):
|
||||||
|
code = []
|
||||||
|
for _ in range(random.randint(0, 2)):
|
||||||
|
if random.randint(0, 3) == 0 and loop_depth < 3:
|
||||||
|
c = gen_code(xl, xr, False, loop_depth + 1)
|
||||||
|
code.append(CodeRepeat(random.randint(0, 3), c, random.randint(0, 2)))
|
||||||
|
elif xr - xl > 1:
|
||||||
|
xmid = random.randrange(xl + 1, xr)
|
||||||
|
ret = random.choice((0, 0, 0, 0, 0, 1, 2))
|
||||||
|
c1 = gen_code(xl, xmid, ret == 1, loop_depth)
|
||||||
|
if random.randrange(5) == 0:
|
||||||
|
c2 = CodeEmpty()
|
||||||
|
else:
|
||||||
|
c2 = gen_code(xmid, xr, ret == 2, loop_depth)
|
||||||
|
code.append(CodeIfRange(xl, xmid, c1, c2))
|
||||||
|
if with_return:
|
||||||
|
if xr - xl == 1:
|
||||||
|
code.append(CodeReturn(random.randrange(10**9)))
|
||||||
|
else:
|
||||||
|
xmid = random.randrange(xl + 1, xr)
|
||||||
|
c1 = gen_code(xl, xmid, True, loop_depth)
|
||||||
|
c2 = gen_code(xmid, xr, True, loop_depth)
|
||||||
|
code.append(CodeIfRange(xl, xmid, c1, c2))
|
||||||
|
for _ in range(random.randint(0, 3)):
|
||||||
|
pos = random.randint(0, len(code))
|
||||||
|
code.insert(pos, CodeAdd(random.randrange(VAR_CNT), random.randint(0, 10**6)))
|
||||||
|
if len(code) == 0:
|
||||||
|
return CodeEmpty()
|
||||||
|
return CodeBlock(code)
|
||||||
|
|
||||||
|
class ExecutionError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def compile_func(fc, fif):
|
||||||
|
res = subprocess.run([FUNC_EXECUTABLE, "-o", fif, "-SPA", fc], capture_output=True)
|
||||||
|
if res.returncode != 0:
|
||||||
|
raise ExecutionError(str(res.stderr, "utf-8"))
|
||||||
|
|
||||||
|
def runvm(compiled_fif, xl, xr):
|
||||||
|
runner = os.path.join(TMP_DIR, "runner.fif")
|
||||||
|
with open(runner, "w") as f:
|
||||||
|
print("\"%s\" include <s constant code" % compiled_fif, file=f)
|
||||||
|
for x in range(xl, xr):
|
||||||
|
print("%d 0 code 1 runvmx abort\"exitcode is not 0\" .s cr { drop } depth 1- times" % x, file=f)
|
||||||
|
res = subprocess.run([FIFT_EXECUTABLE, "-I", FIFT_LIBS, runner], capture_output=True)
|
||||||
|
if res.returncode != 0:
|
||||||
|
raise ExecutionError(str(res.stderr, "utf-8"))
|
||||||
|
output = []
|
||||||
|
for s in str(res.stdout, "utf-8").split("\n"):
|
||||||
|
if s.strip() != "":
|
||||||
|
output.append(list(map(int, s.split())))
|
||||||
|
return output
|
||||||
|
|
||||||
|
cnt_ok = 0
|
||||||
|
cnt_fail = 0
|
||||||
|
for test_id in range(0, 1000000):
|
||||||
|
random.seed(test_id)
|
||||||
|
inline = random.randint(0, 2)
|
||||||
|
xr = random.randint(1, 15)
|
||||||
|
var_idx = 0
|
||||||
|
code = gen_code(0, xr, True)
|
||||||
|
fc = os.path.join(TMP_DIR, "code.fc")
|
||||||
|
fif = os.path.join(TMP_DIR, "compiled.fif")
|
||||||
|
with open(fc, "w") as f:
|
||||||
|
print("int in(int x, int l, int r) impure { return (l <= x) & (x < r); }", file=f)
|
||||||
|
write_function(f, "foo", code, inline=(inline == 1), inline_ref=(inline == 2))
|
||||||
|
print("_ main(int x) {", file=f)
|
||||||
|
print(" (int ret, %s) = foo(x);" % ", ".join("int v%d" % i for i in range(VAR_CNT)), file=f)
|
||||||
|
print(" return (ret, %s, %d);" % (", ".join("v%d" % i for i in range(VAR_CNT)), MAGIC), file=f)
|
||||||
|
print("}", file=f)
|
||||||
|
compile_func(fc, fif)
|
||||||
|
ok = True
|
||||||
|
try:
|
||||||
|
output = runvm(fif, 0, xr)
|
||||||
|
for x in range(xr):
|
||||||
|
my_out = code.execute(State(x)) + [MAGIC]
|
||||||
|
fc_out = output[x]
|
||||||
|
if my_out != fc_out:
|
||||||
|
ok = False
|
||||||
|
break
|
||||||
|
except ExecutionError:
|
||||||
|
ok = False
|
||||||
|
if ok:
|
||||||
|
cnt_ok += 1
|
||||||
|
else:
|
||||||
|
cnt_fail += 1
|
||||||
|
print("Test %-6d %-6s ok:%-6d fail:%-6d" % (test_id, "OK" if ok else "FAIL", cnt_ok, cnt_fail), file=sys.stderr)
|
17
crypto/func/auto-tests/tests/a10.fc
Normal file
17
crypto/func/auto-tests/tests/a10.fc
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
_ main(int a, int x) {
|
||||||
|
int y = 0;
|
||||||
|
int z = 0;
|
||||||
|
while ((y = x * x) > a) {
|
||||||
|
x -= 1;
|
||||||
|
z = 1;
|
||||||
|
}
|
||||||
|
return (y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 0 | 101 15 | 100 1
|
||||||
|
TESTCASE | 0 | 101 14 | 100 1
|
||||||
|
TESTCASE | 0 | 101 10 | 100 0
|
||||||
|
TESTCASE | 0 | 100 10 | 100 0
|
||||||
|
-}
|
89
crypto/func/auto-tests/tests/a6.fc
Normal file
89
crypto/func/auto-tests/tests/a6.fc
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
(int, int) f(int a, int b, int c, int d, int e, int f) {
|
||||||
|
;; solve a 2x2 linear equation
|
||||||
|
int D = a * d - b * c;
|
||||||
|
int Dx = e * d - b * f;
|
||||||
|
int Dy = a * f - e * c;
|
||||||
|
return (Dx / D, Dy / D);
|
||||||
|
}
|
||||||
|
|
||||||
|
int calc_phi() {
|
||||||
|
var n = 1;
|
||||||
|
repeat (70) { n *= 10; }
|
||||||
|
var p = var q = 1;
|
||||||
|
do {
|
||||||
|
(p, q) = (q, p + q);
|
||||||
|
} until (q > n);
|
||||||
|
return muldivr(p, n, q);
|
||||||
|
}
|
||||||
|
|
||||||
|
int calc_sqrt2() {
|
||||||
|
var n = 1;
|
||||||
|
repeat (70) { n *= 10; }
|
||||||
|
var p = var q = 1;
|
||||||
|
do {
|
||||||
|
var t = p + q;
|
||||||
|
(p, q) = (q, t + q);
|
||||||
|
} until (q > n);
|
||||||
|
return muldivr(p, n, q);
|
||||||
|
}
|
||||||
|
|
||||||
|
var calc_root(m) {
|
||||||
|
int base = 1;
|
||||||
|
repeat(70) { base *= 10; }
|
||||||
|
var (a, b, c) = (1, 0, - m);
|
||||||
|
var (p1, q1, p2, q2) = (1, 0, 0, 1);
|
||||||
|
do {
|
||||||
|
int k = -1;
|
||||||
|
var (a1, b1, c1) = (0, 0, 0);
|
||||||
|
do {
|
||||||
|
k += 1;
|
||||||
|
(a1, b1, c1) = (a, b, c);
|
||||||
|
c += b;
|
||||||
|
c += b += a;
|
||||||
|
} until (c > 0);
|
||||||
|
(a, b, c) = (- c1, - b1, - a1);
|
||||||
|
(p1, q1) = (k * p1 + q1, p1);
|
||||||
|
(p2, q2) = (k * p2 + q2, p2);
|
||||||
|
} until (p1 > base);
|
||||||
|
return (p1, q1, p2, q2);
|
||||||
|
}
|
||||||
|
|
||||||
|
{-
|
||||||
|
operator _/%_ infix 20;
|
||||||
|
|
||||||
|
(int, int) ((int x) /% (int y)) {
|
||||||
|
return (x / y, x % y);
|
||||||
|
}
|
||||||
|
|
||||||
|
(int, int) _/%_ (int x, int y) {
|
||||||
|
return (x / y, x % y);
|
||||||
|
}
|
||||||
|
-}
|
||||||
|
|
||||||
|
int ataninv(int base, int q) { ;; computes base*atan(1/q)
|
||||||
|
base ~/= q;
|
||||||
|
q *= - q;
|
||||||
|
int sum = 0;
|
||||||
|
int n = 1;
|
||||||
|
do {
|
||||||
|
sum += base ~/ n;
|
||||||
|
base ~/= q;
|
||||||
|
n += 2;
|
||||||
|
} until base == 0;
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
int calc_pi() {
|
||||||
|
int base = 64;
|
||||||
|
repeat (70) { base *= 10; }
|
||||||
|
return (ataninv(base << 2, 5) - ataninv(base, 239)) ~>> 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
return calc_pi();
|
||||||
|
}
|
||||||
|
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 0 | | 31415926535897932384626433832795028841971693993751058209749445923078164
|
||||||
|
-}
|
16
crypto/func/auto-tests/tests/a6_1.fc
Normal file
16
crypto/func/auto-tests/tests/a6_1.fc
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
(int, int) main(int a, int b, int c, int d, int e, int f) {
|
||||||
|
int D = a * d - b * c;
|
||||||
|
int Dx = e * d - b * f;
|
||||||
|
int Dy = a * f - e * c;
|
||||||
|
return (Dx / D, Dy / D);
|
||||||
|
}
|
||||||
|
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 0 | 1 1 1 -1 10 6 | 8 2
|
||||||
|
TESTCASE | 0 | 817 -31 624 -241 132272 272276 | 132 -788
|
||||||
|
TESTCASE | 0 | -886 562 498 -212 -36452 -68958 | -505 -861
|
||||||
|
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
|
||||||
|
-}
|
24
crypto/func/auto-tests/tests/a6_5.fc
Normal file
24
crypto/func/auto-tests/tests/a6_5.fc
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
var twice(f, x) {
|
||||||
|
return f (f x);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ sqr(x) {
|
||||||
|
return x * x;
|
||||||
|
}
|
||||||
|
|
||||||
|
var main(x) {
|
||||||
|
var f = sqr;
|
||||||
|
return twice(f, x) * f(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pow6(x) method_id(4) {
|
||||||
|
return twice(sqr, x) * sqr(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 0 | 3 | 729
|
||||||
|
TESTCASE | 0 | 10 | 1000000
|
||||||
|
TESTCASE | 4 | 3 | 729
|
||||||
|
TESTCASE | 4 | 10 | 1000000
|
||||||
|
-}
|
24
crypto/func/auto-tests/tests/a7.fc
Normal file
24
crypto/func/auto-tests/tests/a7.fc
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
() main() { }
|
||||||
|
int steps(int x) method_id(1) {
|
||||||
|
var n = 0;
|
||||||
|
while (x > 1) {
|
||||||
|
n += 1;
|
||||||
|
if (x & 1) {
|
||||||
|
x = 3 * x + 1;
|
||||||
|
} else {
|
||||||
|
x >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 1 | 1 | 0
|
||||||
|
TESTCASE | 1 | 2 | 1
|
||||||
|
TESTCASE | 1 | 5 | 5
|
||||||
|
TESTCASE | 1 | 19 | 20
|
||||||
|
TESTCASE | 1 | 27 | 111
|
||||||
|
TESTCASE | 1 | 100 | 25
|
||||||
|
-}
|
||||||
|
|
17
crypto/func/auto-tests/tests/c2.fc
Normal file
17
crypto/func/auto-tests/tests/c2.fc
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
global ((int, int) -> int) op;
|
||||||
|
|
||||||
|
int check_assoc(int a, int b, int c) {
|
||||||
|
return op(op(a, b), c) == op(a, op(b, c));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int x, int y, int z) {
|
||||||
|
op = _+_;
|
||||||
|
return check_assoc(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 0 | 2 3 9 | -1
|
||||||
|
TESTCASE | 0 | 11 22 44 | -1
|
||||||
|
TESTCASE | 0 | -1 -10 -20 | -1
|
||||||
|
-}
|
14
crypto/func/auto-tests/tests/c2_1.fc
Normal file
14
crypto/func/auto-tests/tests/c2_1.fc
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
_ check_assoc(op, a, b, c) {
|
||||||
|
return op(op(a, b), c) == op(a, op(b, c));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int x, int y, int z) {
|
||||||
|
return check_assoc(_+_, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 0 | 2 3 9 | -1
|
||||||
|
TESTCASE | 0 | 11 22 44 | -1
|
||||||
|
TESTCASE | 0 | -1 -10 -20 | -1
|
||||||
|
-}
|
60
crypto/func/auto-tests/tests/co1.fc
Normal file
60
crypto/func/auto-tests/tests/co1.fc
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
const int1 = 1, int2 = 2;
|
||||||
|
|
||||||
|
const int int101 = 101;
|
||||||
|
const int int111 = 111;
|
||||||
|
|
||||||
|
const int1r = int1;
|
||||||
|
|
||||||
|
const str1 = "const1", str2 = "aabbcc"s;
|
||||||
|
|
||||||
|
const slice str2r = str2;
|
||||||
|
|
||||||
|
const str1int = 0x636f6e737431;
|
||||||
|
const str2int = 0xAABBCC;
|
||||||
|
|
||||||
|
const int nibbles = 4;
|
||||||
|
|
||||||
|
int iget1() { return int1; }
|
||||||
|
int iget2() { return int2; }
|
||||||
|
int iget3() { return int1 + int2; }
|
||||||
|
|
||||||
|
int iget1r() { return int1r; }
|
||||||
|
|
||||||
|
slice sget1() { return str1; }
|
||||||
|
slice sget2() { return str2; }
|
||||||
|
slice sget2r() { return str2r; }
|
||||||
|
|
||||||
|
const int int240 = ((int1 + int2) * 10) << 3;
|
||||||
|
|
||||||
|
int iget240() { return int240; }
|
||||||
|
|
||||||
|
builder newc() asm "NEWC";
|
||||||
|
slice endcs(builder b) asm "ENDC" "CTOS";
|
||||||
|
int sdeq (slice s1, slice s2) asm "SDEQ";
|
||||||
|
builder stslicer(builder b, slice s) asm "STSLICER";
|
||||||
|
|
||||||
|
_ main() {
|
||||||
|
int i1 = iget1();
|
||||||
|
int i2 = iget2();
|
||||||
|
int i3 = iget3();
|
||||||
|
|
||||||
|
throw_unless(int101, i1 == 1);
|
||||||
|
throw_unless(102, i2 == 2);
|
||||||
|
throw_unless(103, i3 == 3);
|
||||||
|
|
||||||
|
slice s1 = sget1();
|
||||||
|
slice s2 = sget2();
|
||||||
|
slice s3 = newc().stslicer(str1).stslicer(str2r).endcs();
|
||||||
|
|
||||||
|
throw_unless(int111, sdeq(s1, newc().store_uint(str1int, 12 * nibbles).endcs()));
|
||||||
|
throw_unless(112, sdeq(s2, newc().store_uint(str2int, 6 * nibbles).endcs()));
|
||||||
|
throw_unless(113, sdeq(s3, newc().store_uint(0x636f6e737431AABBCC, 18 * nibbles).endcs()));
|
||||||
|
|
||||||
|
int i4 = iget240();
|
||||||
|
throw_unless(104, i4 == 240);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
{-
|
||||||
|
TESTCASE | 0 | | 0
|
||||||
|
-}
|
19
crypto/func/auto-tests/tests/code_after_ifelse.fc
Normal file
19
crypto/func/auto-tests/tests/code_after_ifelse.fc
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
int foo(int x) inline method_id(1) {
|
||||||
|
if (x == 1) {
|
||||||
|
return 111;
|
||||||
|
} else {
|
||||||
|
x *= 2;
|
||||||
|
}
|
||||||
|
return x + 1;
|
||||||
|
}
|
||||||
|
(int, int) main(int x) {
|
||||||
|
return (foo(x), 222);
|
||||||
|
}
|
||||||
|
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 1 | 1 | 111
|
||||||
|
TESTCASE | 1 | 3 | 7
|
||||||
|
TESTCASE | 0 | 1 | 111 222
|
||||||
|
TESTCASE | 0 | 3 | 7 222
|
||||||
|
-}
|
61
crypto/func/auto-tests/tests/inline_big.fc
Normal file
61
crypto/func/auto-tests/tests/inline_big.fc
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
int foo(int x) inline {
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
x = x * 10 + 1;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
(int) main(int x) {
|
||||||
|
return foo(x) * 10 + 5;
|
||||||
|
}
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 0 | 9 | 9111111111111111111111111111111111111111111111111115
|
||||||
|
-}
|
26
crypto/func/auto-tests/tests/inline_if.fc
Normal file
26
crypto/func/auto-tests/tests/inline_if.fc
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
int foo1(int x) {
|
||||||
|
if (x == 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
int foo2(int x) inline {
|
||||||
|
if (x == 1) {
|
||||||
|
return 11;
|
||||||
|
}
|
||||||
|
return 22;
|
||||||
|
}
|
||||||
|
int foo3(int x) inline_ref {
|
||||||
|
if (x == 1) {
|
||||||
|
return 111;
|
||||||
|
}
|
||||||
|
return 222;
|
||||||
|
}
|
||||||
|
(int, int, int) main(int x) {
|
||||||
|
return (foo1(x) + 1, foo2(x) + 1, foo3(x) + 1);
|
||||||
|
}
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 0 | 1 | 2 12 112
|
||||||
|
TESTCASE | 0 | 2 | 3 23 223
|
||||||
|
-}
|
43
crypto/func/auto-tests/tests/inline_loops.fc
Normal file
43
crypto/func/auto-tests/tests/inline_loops.fc
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
global int g;
|
||||||
|
|
||||||
|
_ foo_repeat() impure inline {
|
||||||
|
g = 1;
|
||||||
|
repeat(5) {
|
||||||
|
g *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int foo_until() impure inline {
|
||||||
|
g = 1;
|
||||||
|
int i = 0;
|
||||||
|
do {
|
||||||
|
g *= 2;
|
||||||
|
i += 1;
|
||||||
|
} until (i >= 8);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int foo_while() impure inline {
|
||||||
|
g = 1;
|
||||||
|
int i = 0;
|
||||||
|
while (i < 10) {
|
||||||
|
g *= 2;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ main() {
|
||||||
|
foo_repeat();
|
||||||
|
int x = g;
|
||||||
|
foo_until();
|
||||||
|
int y = g;
|
||||||
|
foo_while();
|
||||||
|
int z = g;
|
||||||
|
return (x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 0 | | 32 256 1024
|
||||||
|
-}
|
12
crypto/func/auto-tests/tests/method_id.fc
Normal file
12
crypto/func/auto-tests/tests/method_id.fc
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
int foo1() method_id(1) { return 111; }
|
||||||
|
int foo2() method_id(3) { return 222; }
|
||||||
|
int foo3() method_id(10) { return 333; }
|
||||||
|
int main() { return 999; }
|
||||||
|
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 1 | | 111
|
||||||
|
TESTCASE | 3 | | 222
|
||||||
|
TESTCASE | 10 | | 333
|
||||||
|
TESTCASE | 0 | | 999
|
||||||
|
-}
|
54
crypto/func/auto-tests/tests/s1.fc
Normal file
54
crypto/func/auto-tests/tests/s1.fc
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
slice ascii_slice() method_id {
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
slice raw_slice() method_id {
|
||||||
|
return "abcdef"s;
|
||||||
|
}
|
||||||
|
|
||||||
|
slice addr_slice() method_id {
|
||||||
|
return "Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF"a;
|
||||||
|
}
|
||||||
|
|
||||||
|
int string_hex() method_id {
|
||||||
|
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"u;
|
||||||
|
}
|
||||||
|
|
||||||
|
int string_minihash() method_id {
|
||||||
|
return "transfer(slice, int)"h;
|
||||||
|
}
|
||||||
|
|
||||||
|
int string_maxihash() method_id {
|
||||||
|
return "transfer(slice, int)"H;
|
||||||
|
}
|
||||||
|
|
||||||
|
int string_crc32() method_id {
|
||||||
|
return "transfer(slice, int)"c;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder newc() asm "NEWC";
|
||||||
|
slice endcs(builder b) asm "ENDC" "CTOS";
|
||||||
|
int sdeq (slice s1, slice s2) asm "SDEQ";
|
||||||
|
|
||||||
|
_ main() {
|
||||||
|
slice s_ascii = ascii_slice();
|
||||||
|
slice s_raw = raw_slice();
|
||||||
|
slice s_addr = addr_slice();
|
||||||
|
int i_hex = string_hex();
|
||||||
|
int i_mini = string_minihash();
|
||||||
|
int i_maxi = string_maxihash();
|
||||||
|
int i_crc = string_crc32();
|
||||||
|
throw_unless(101, sdeq(s_ascii, newc().store_uint(0x737472696E67, 12 * 4).endcs()));
|
||||||
|
throw_unless(102, sdeq(s_raw, newc().store_uint(0xABCDEF, 6 * 4).endcs()));
|
||||||
|
throw_unless(103, sdeq(s_addr, newc().store_uint(4, 3).store_int(-1, 8)
|
||||||
|
.store_uint(0x3333333333333333333333333333333333333333333333333333333333333333, 256).endcs()));
|
||||||
|
throw_unless(104, i_hex == 0x4142434445464748494A4B4C4D4E4F505152535455565758595A303132333435);
|
||||||
|
throw_unless(105, i_mini == 0x7a62e8a8);
|
||||||
|
throw_unless(106, i_maxi == 0x7a62e8a8ebac41bd6de16c65e7be363bc2d2cbc6a0873778dead4795c13db979);
|
||||||
|
throw_unless(107, i_crc == 2235694568);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
{-
|
||||||
|
TESTCASE | 0 | | 0
|
||||||
|
-}
|
17
crypto/func/auto-tests/tests/unbalanced_ret.fc
Normal file
17
crypto/func/auto-tests/tests/unbalanced_ret.fc
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
(int, int) main(int x) {
|
||||||
|
int y = 5;
|
||||||
|
if (x < 0) {
|
||||||
|
x *= 2;
|
||||||
|
y += 1;
|
||||||
|
if (x == -10) {
|
||||||
|
return (111, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (x + 1, y);
|
||||||
|
}
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 0 | 10 | 11 5
|
||||||
|
TESTCASE | 0 | -5 | 111 0
|
||||||
|
TESTCASE | 0 | -4 | -7 6
|
||||||
|
-}
|
18
crypto/func/auto-tests/tests/unbalanced_ret_inline.fc
Normal file
18
crypto/func/auto-tests/tests/unbalanced_ret_inline.fc
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
int foo(int x) inline {
|
||||||
|
if (x < 0) {
|
||||||
|
x *= 2;
|
||||||
|
if (x == -10) {
|
||||||
|
return 111;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return x + 1;
|
||||||
|
}
|
||||||
|
int main(int x) {
|
||||||
|
return foo(x) * 10;
|
||||||
|
}
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 0 | 10 | 110
|
||||||
|
TESTCASE | 0 | -5 | 1110
|
||||||
|
TESTCASE | 0 | -4 | -70
|
||||||
|
-}
|
48
crypto/func/auto-tests/tests/unbalanced_ret_loops.fc
Normal file
48
crypto/func/auto-tests/tests/unbalanced_ret_loops.fc
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
_ main() { }
|
||||||
|
|
||||||
|
int foo_repeat(int x) method_id(1) {
|
||||||
|
repeat(10) {
|
||||||
|
x += 10;
|
||||||
|
if (x >= 100) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int foo_while(int x) method_id(2) {
|
||||||
|
int i = 0;
|
||||||
|
while (i < 10) {
|
||||||
|
x += 10;
|
||||||
|
if (x >= 100) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int foo_until(int x) method_id(3) {
|
||||||
|
int i = 0;
|
||||||
|
do {
|
||||||
|
x += 10;
|
||||||
|
if (x >= 100) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
} until (i >= 10);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 1 | 40 | 100
|
||||||
|
TESTCASE | 1 | 33 | 103
|
||||||
|
TESTCASE | 1 | -5 | -1
|
||||||
|
TESTCASE | 2 | 40 | 100
|
||||||
|
TESTCASE | 2 | 33 | 103
|
||||||
|
TESTCASE | 2 | -5 | -1
|
||||||
|
TESTCASE | 3 | 40 | 100
|
||||||
|
TESTCASE | 3 | 33 | 103
|
||||||
|
TESTCASE | 3 | -5 | -1
|
||||||
|
-}
|
35
crypto/func/auto-tests/tests/unbalanced_ret_nested.fc
Normal file
35
crypto/func/auto-tests/tests/unbalanced_ret_nested.fc
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
int foo(int y) {
|
||||||
|
if (y < 0) {
|
||||||
|
y *= 2;
|
||||||
|
if (y == -10) {
|
||||||
|
return 111;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return y + 1;
|
||||||
|
}
|
||||||
|
(int, int) bar(int x, int y) {
|
||||||
|
if (x < 0) {
|
||||||
|
y = foo(y);
|
||||||
|
x *= 2;
|
||||||
|
if (x == -10) {
|
||||||
|
return (111, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (x + 1, y);
|
||||||
|
}
|
||||||
|
(int, int) main(int x, int y) {
|
||||||
|
(x, y) = bar(x, y);
|
||||||
|
return (x, y * 10);
|
||||||
|
}
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 0 | 3 3 | 4 30
|
||||||
|
TESTCASE | 0 | 3 -5 | 4 -50
|
||||||
|
TESTCASE | 0 | 3 -4 | 4 -40
|
||||||
|
TESTCASE | 0 | -5 3 | 111 40
|
||||||
|
TESTCASE | 0 | -5 -5 | 111 1110
|
||||||
|
TESTCASE | 0 | -5 -4 | 111 -70
|
||||||
|
TESTCASE | 0 | -4 3 | -7 40
|
||||||
|
TESTCASE | 0 | -4 -5 | -7 1110
|
||||||
|
TESTCASE | 0 | -4 -4 | -7 -70
|
||||||
|
-}
|
14
crypto/func/auto-tests/tests/w1.fc
Normal file
14
crypto/func/auto-tests/tests/w1.fc
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
(int, int) main(int id) {
|
||||||
|
if (id > 0) {
|
||||||
|
if (id > 10) {
|
||||||
|
return (2 * id, 3 * id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (5, 6);
|
||||||
|
}
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 0 | 0 | 5 6
|
||||||
|
TESTCASE | 0 | 4 | 5 6
|
||||||
|
TESTCASE | 0 | 11 | 22 33
|
||||||
|
-}
|
18
crypto/func/auto-tests/tests/w2.fc
Normal file
18
crypto/func/auto-tests/tests/w2.fc
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
_ f(cs) {
|
||||||
|
return (cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8),
|
||||||
|
cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8),
|
||||||
|
cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8),
|
||||||
|
cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8),
|
||||||
|
cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8));
|
||||||
|
}
|
||||||
|
|
||||||
|
_ main(cs) {
|
||||||
|
var (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10,
|
||||||
|
x11, x12, x13, x14, x15, x16, x17, x18, x19) = f(cs);
|
||||||
|
return x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9
|
||||||
|
+ x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19;
|
||||||
|
}
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 0 | x{000102030405060708090a0b0c0d0e0f10111213} | 190
|
||||||
|
-}
|
17
crypto/func/auto-tests/tests/w6.fc
Normal file
17
crypto/func/auto-tests/tests/w6.fc
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
int main(int x) {
|
||||||
|
int i = 0;
|
||||||
|
;; int f = false;
|
||||||
|
do {
|
||||||
|
i = i + 1;
|
||||||
|
if (i > 5) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int f = (i * i == 64);
|
||||||
|
} until (f);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 0 | 0 | 1
|
||||||
|
-}
|
24
crypto/func/auto-tests/tests/w7.fc
Normal file
24
crypto/func/auto-tests/tests/w7.fc
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
int test(int y) method_id(1) {
|
||||||
|
int x = 1;
|
||||||
|
if (y > 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return x > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int f(int y) method_id(2) {
|
||||||
|
if (y > 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ main() { }
|
||||||
|
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 1 | 10 | 1
|
||||||
|
TESTCASE | 1 | -5 | -1
|
||||||
|
TESTCASE | 2 | 10 | 1
|
||||||
|
TESTCASE | 2 | -5 | 2
|
||||||
|
-}
|
14
crypto/func/auto-tests/tests/w9.fc
Normal file
14
crypto/func/auto-tests/tests/w9.fc
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
_ main(s) {
|
||||||
|
var (z, t) = (17, s);
|
||||||
|
while (z > 0) {
|
||||||
|
t = s;
|
||||||
|
z -= 1;
|
||||||
|
}
|
||||||
|
return ~ t;
|
||||||
|
}
|
||||||
|
|
||||||
|
{-
|
||||||
|
method_id | in | out
|
||||||
|
TESTCASE | 0 | 1 | -2
|
||||||
|
TESTCASE | 0 | 5 | -6
|
||||||
|
-}
|
|
@ -173,6 +173,74 @@ int emulate_mul(int a, int b) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int emulate_and(int a, int b) {
|
||||||
|
int both = a & b, any = a | b;
|
||||||
|
int r = VarDescr::_Int;
|
||||||
|
if (any & VarDescr::_Nan) {
|
||||||
|
return r | VarDescr::_Nan;
|
||||||
|
}
|
||||||
|
r |= VarDescr::_Finite;
|
||||||
|
if (any & VarDescr::_Zero) {
|
||||||
|
return VarDescr::ConstZero;
|
||||||
|
}
|
||||||
|
r |= both & (VarDescr::_Even | VarDescr::_Odd);
|
||||||
|
r |= both & (VarDescr::_Bit | VarDescr::_Bool);
|
||||||
|
if (both & VarDescr::_Odd) {
|
||||||
|
r |= VarDescr::_NonZero;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int emulate_or(int a, int b) {
|
||||||
|
if (b & VarDescr::_Zero) {
|
||||||
|
return a;
|
||||||
|
} else if (a & VarDescr::_Zero) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
int both = a & b, any = a | b;
|
||||||
|
int r = VarDescr::_Int;
|
||||||
|
if (any & VarDescr::_Nan) {
|
||||||
|
return r | VarDescr::_Nan;
|
||||||
|
}
|
||||||
|
r |= VarDescr::_Finite;
|
||||||
|
r |= any & VarDescr::_NonZero;
|
||||||
|
r |= any & VarDescr::_Odd;
|
||||||
|
r |= both & VarDescr::_Even;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int emulate_xor(int a, int b) {
|
||||||
|
if (b & VarDescr::_Zero) {
|
||||||
|
return a;
|
||||||
|
} else if (a & VarDescr::_Zero) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
int both = a & b, any = a | b;
|
||||||
|
int r = VarDescr::_Int;
|
||||||
|
if (any & VarDescr::_Nan) {
|
||||||
|
return r | VarDescr::_Nan;
|
||||||
|
}
|
||||||
|
r |= VarDescr::_Finite;
|
||||||
|
r |= both & VarDescr::_Even;
|
||||||
|
if (both & VarDescr::_Odd) {
|
||||||
|
r |= VarDescr::_Even;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int emulate_not(int a) {
|
||||||
|
int f = VarDescr::_Even | VarDescr::_Odd;
|
||||||
|
if ((a & f) && (~a & f)) {
|
||||||
|
a ^= f;
|
||||||
|
}
|
||||||
|
f = VarDescr::_Pos | VarDescr::_Neg;
|
||||||
|
if ((a & f) && (~a & f)) {
|
||||||
|
a ^= f;
|
||||||
|
}
|
||||||
|
a &= ~(VarDescr::_Zero | VarDescr::_NonZero | VarDescr::_Bit);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
int emulate_lshift(int a, int b) {
|
int emulate_lshift(int a, int b) {
|
||||||
if (((a | b) & VarDescr::_Nan) || !(~b & (VarDescr::_Neg | VarDescr::_NonZero))) {
|
if (((a | b) & VarDescr::_Nan) || !(~b & (VarDescr::_Neg | VarDescr::_NonZero))) {
|
||||||
return VarDescr::_Int | VarDescr::_Nan;
|
return VarDescr::_Int | VarDescr::_Nan;
|
||||||
|
@ -427,6 +495,57 @@ AsmOp compile_negate(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
||||||
return exec_op("NEGATE", 1);
|
return exec_op("NEGATE", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AsmOp compile_and(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
||||||
|
assert(res.size() == 1 && args.size() == 2);
|
||||||
|
VarDescr &r = res[0], &x = args[0], &y = args[1];
|
||||||
|
if (x.is_int_const() && y.is_int_const()) {
|
||||||
|
r.set_const(x.int_const & y.int_const);
|
||||||
|
x.unused();
|
||||||
|
y.unused();
|
||||||
|
return push_const(r.int_const);
|
||||||
|
}
|
||||||
|
r.val = emulate_and(x.val, y.val);
|
||||||
|
return exec_op("AND", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsmOp compile_or(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
||||||
|
assert(res.size() == 1 && args.size() == 2);
|
||||||
|
VarDescr &r = res[0], &x = args[0], &y = args[1];
|
||||||
|
if (x.is_int_const() && y.is_int_const()) {
|
||||||
|
r.set_const(x.int_const | y.int_const);
|
||||||
|
x.unused();
|
||||||
|
y.unused();
|
||||||
|
return push_const(r.int_const);
|
||||||
|
}
|
||||||
|
r.val = emulate_or(x.val, y.val);
|
||||||
|
return exec_op("OR", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsmOp compile_xor(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
||||||
|
assert(res.size() == 1 && args.size() == 2);
|
||||||
|
VarDescr &r = res[0], &x = args[0], &y = args[1];
|
||||||
|
if (x.is_int_const() && y.is_int_const()) {
|
||||||
|
r.set_const(x.int_const ^ y.int_const);
|
||||||
|
x.unused();
|
||||||
|
y.unused();
|
||||||
|
return push_const(r.int_const);
|
||||||
|
}
|
||||||
|
r.val = emulate_xor(x.val, y.val);
|
||||||
|
return exec_op("XOR", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsmOp compile_not(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
||||||
|
assert(res.size() == 1 && args.size() == 1);
|
||||||
|
VarDescr &r = res[0], &x = args[0];
|
||||||
|
if (x.is_int_const()) {
|
||||||
|
r.set_const(~x.int_const);
|
||||||
|
x.unused();
|
||||||
|
return push_const(r.int_const);
|
||||||
|
}
|
||||||
|
r.val = emulate_not(x.val);
|
||||||
|
return exec_op("NOT", 1);
|
||||||
|
}
|
||||||
|
|
||||||
AsmOp compile_mul_internal(VarDescr& r, VarDescr& x, VarDescr& y) {
|
AsmOp compile_mul_internal(VarDescr& r, VarDescr& x, VarDescr& y) {
|
||||||
if (x.is_int_const() && y.is_int_const()) {
|
if (x.is_int_const() && y.is_int_const()) {
|
||||||
r.set_const(x.int_const * y.int_const);
|
r.set_const(x.int_const * y.int_const);
|
||||||
|
@ -442,6 +561,7 @@ AsmOp compile_mul_internal(VarDescr& r, VarDescr& x, VarDescr& y) {
|
||||||
if (y.always_zero() && x.always_finite()) {
|
if (y.always_zero() && x.always_finite()) {
|
||||||
// dubious optimization: NaN * 0 = ?
|
// dubious optimization: NaN * 0 = ?
|
||||||
r.set_const(y.int_const);
|
r.set_const(y.int_const);
|
||||||
|
x.unused();
|
||||||
return push_const(r.int_const);
|
return push_const(r.int_const);
|
||||||
}
|
}
|
||||||
if (*y.int_const == 1 && x.always_finite()) {
|
if (*y.int_const == 1 && x.always_finite()) {
|
||||||
|
@ -468,6 +588,7 @@ AsmOp compile_mul_internal(VarDescr& r, VarDescr& x, VarDescr& y) {
|
||||||
if (x.always_zero() && y.always_finite()) {
|
if (x.always_zero() && y.always_finite()) {
|
||||||
// dubious optimization: NaN * 0 = ?
|
// dubious optimization: NaN * 0 = ?
|
||||||
r.set_const(x.int_const);
|
r.set_const(x.int_const);
|
||||||
|
y.unused();
|
||||||
return push_const(r.int_const);
|
return push_const(r.int_const);
|
||||||
}
|
}
|
||||||
if (*x.int_const == 1 && y.always_finite()) {
|
if (*x.int_const == 1 && y.always_finite()) {
|
||||||
|
@ -1000,10 +1121,10 @@ void define_builtins() {
|
||||||
define_builtin_func("_>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, -1));
|
define_builtin_func("_>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, -1));
|
||||||
define_builtin_func("_~>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, 0));
|
define_builtin_func("_~>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, 0));
|
||||||
define_builtin_func("_^>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1));
|
define_builtin_func("_^>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1));
|
||||||
define_builtin_func("_&_", arith_bin_op, AsmOp::Custom("AND", 2));
|
define_builtin_func("_&_", arith_bin_op, compile_and);
|
||||||
define_builtin_func("_|_", arith_bin_op, AsmOp::Custom("OR", 2));
|
define_builtin_func("_|_", arith_bin_op, compile_or);
|
||||||
define_builtin_func("_^_", arith_bin_op, AsmOp::Custom("XOR", 2));
|
define_builtin_func("_^_", arith_bin_op, compile_xor);
|
||||||
define_builtin_func("~_", arith_un_op, AsmOp::Custom("NOT", 1));
|
define_builtin_func("~_", arith_un_op, compile_not);
|
||||||
define_builtin_func("^_+=_", arith_bin_op, compile_add);
|
define_builtin_func("^_+=_", arith_bin_op, compile_add);
|
||||||
define_builtin_func("^_-=_", arith_bin_op, compile_sub);
|
define_builtin_func("^_-=_", arith_bin_op, compile_sub);
|
||||||
define_builtin_func("^_*=_", arith_bin_op, compile_mul);
|
define_builtin_func("^_*=_", arith_bin_op, compile_mul);
|
||||||
|
@ -1017,9 +1138,9 @@ void define_builtins() {
|
||||||
define_builtin_func("^_>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, -1));
|
define_builtin_func("^_>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, -1));
|
||||||
define_builtin_func("^_~>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 0));
|
define_builtin_func("^_~>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 0));
|
||||||
define_builtin_func("^_^>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1));
|
define_builtin_func("^_^>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1));
|
||||||
define_builtin_func("^_&=_", arith_bin_op, AsmOp::Custom("AND", 2));
|
define_builtin_func("^_&=_", arith_bin_op, compile_and);
|
||||||
define_builtin_func("^_|=_", arith_bin_op, AsmOp::Custom("OR", 2));
|
define_builtin_func("^_|=_", arith_bin_op, compile_or);
|
||||||
define_builtin_func("^_^=_", arith_bin_op, AsmOp::Custom("XOR", 2));
|
define_builtin_func("^_^=_", arith_bin_op, compile_xor);
|
||||||
define_builtin_func("muldiv", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, -1));
|
define_builtin_func("muldiv", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, -1));
|
||||||
define_builtin_func("muldivr", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, 0));
|
define_builtin_func("muldivr", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, 0));
|
||||||
define_builtin_func("muldivc", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, 1));
|
define_builtin_func("muldivc", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, 1));
|
||||||
|
@ -1063,6 +1184,8 @@ void define_builtins() {
|
||||||
AsmOp::Nop());
|
AsmOp::Nop());
|
||||||
define_builtin_func("~dump", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))),
|
define_builtin_func("~dump", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))),
|
||||||
AsmOp::Custom("s0 DUMP", 1, 1), true);
|
AsmOp::Custom("s0 DUMP", 1, 1), true);
|
||||||
|
define_builtin_func("~strdump", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))),
|
||||||
|
AsmOp::Custom("STRDUMP", 1, 1), true);
|
||||||
define_builtin_func("run_method0", TypeExpr::new_map(Int, Unit),
|
define_builtin_func("run_method0", TypeExpr::new_map(Int, Unit),
|
||||||
[](auto a, auto b, auto c) { return compile_run_method(a, b, c, 0, false); }, true);
|
[](auto a, auto b, auto c) { return compile_run_method(a, b, c, 0, false); }, true);
|
||||||
define_builtin_func("run_method1", TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X}), Unit)),
|
define_builtin_func("run_method1", TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X}), Unit)),
|
||||||
|
|
127
crypto/func/func-main.cpp
Normal file
127
crypto/func/func-main.cpp
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
This file is part of TON Blockchain source code.
|
||||||
|
|
||||||
|
TON Blockchain is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU 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 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
#include "func.h"
|
||||||
|
#include "parser/srcread.h"
|
||||||
|
#include "parser/lexer.h"
|
||||||
|
#include "parser/symtable.h"
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include "git.h"
|
||||||
|
|
||||||
|
void usage(const char* progname) {
|
||||||
|
std::cerr
|
||||||
|
<< "usage: " << progname
|
||||||
|
<< " [-vIAPSR][-O<level>][-i<indent-spc>][-o<output-filename>][-W<boc-filename>] {<func-source-filename> ...}\n"
|
||||||
|
"\tGenerates Fift TVM assembler code from a funC source\n"
|
||||||
|
"-I\tEnables interactive mode (parse stdin)\n"
|
||||||
|
"-o<fift-output-filename>\tWrites generated code into specified file instead of stdout\n"
|
||||||
|
"-v\tIncreases verbosity level (extra information output into stderr)\n"
|
||||||
|
"-i<indent>\tSets indentation for the output code (in two-space units)\n"
|
||||||
|
"-A\tPrefix code with `\"Asm.fif\" include` preamble\n"
|
||||||
|
"-O<level>\tSets optimization level (2 by default)\n"
|
||||||
|
"-P\tEnvelope code into PROGRAM{ ... }END>c\n"
|
||||||
|
"-S\tInclude stack layout comments in the output code\n"
|
||||||
|
"-R\tInclude operation rewrite comments in the output code\n"
|
||||||
|
"-W<output-boc-file>\tInclude Fift code to serialize and save generated code into specified BoC file. Enables "
|
||||||
|
"-A and -P.\n"
|
||||||
|
"\t-s\tOutput semantic version of FunC and exit\n"
|
||||||
|
"\t-V<version>\tShow func build information\n";
|
||||||
|
std::exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* const argv[]) {
|
||||||
|
int i;
|
||||||
|
std::string output_filename;
|
||||||
|
while ((i = getopt(argc, argv, "Ahi:Io:O:PRsSvW:V")) != -1) {
|
||||||
|
switch (i) {
|
||||||
|
case 'A':
|
||||||
|
funC::asm_preamble = true;
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
funC::interactive = true;
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
funC::indent = std::max(0, atoi(optarg));
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
output_filename = optarg;
|
||||||
|
break;
|
||||||
|
case 'O':
|
||||||
|
funC::opt_level = std::max(0, atoi(optarg));
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
funC::program_envelope = true;
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
funC::op_rewrite_comments = true;
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
funC::stack_layout_comments = true;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
++funC::verbosity;
|
||||||
|
break;
|
||||||
|
case 'W':
|
||||||
|
funC::boc_output_filename = optarg;
|
||||||
|
funC::asm_preamble = funC::program_envelope = true;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
std::cout << funC::func_version << "\n";
|
||||||
|
std::exit(0);
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
std::cout << "FunC semantic version: v" << funC::func_version << "\n";
|
||||||
|
std::cout << "Build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n";
|
||||||
|
std::exit(0);
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
default:
|
||||||
|
usage(argv[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream *outs = &std::cout;
|
||||||
|
|
||||||
|
std::unique_ptr<std::fstream> fs;
|
||||||
|
if (!output_filename.empty()) {
|
||||||
|
fs = std::make_unique<std::fstream>(output_filename, fs->trunc | fs->out);
|
||||||
|
if (!fs->is_open()) {
|
||||||
|
std::cerr << "failed to create output file " << output_filename << '\n';
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
outs = fs.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> sources;
|
||||||
|
|
||||||
|
while (optind < argc) {
|
||||||
|
sources.push_back(std::string(argv[optind++]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return funC::func_proceed(sources, *outs, std::cerr);
|
||||||
|
}
|
|
@ -28,16 +28,14 @@
|
||||||
#include "func.h"
|
#include "func.h"
|
||||||
#include "parser/srcread.h"
|
#include "parser/srcread.h"
|
||||||
#include "parser/lexer.h"
|
#include "parser/lexer.h"
|
||||||
#include "parser/symtable.h"
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <fstream>
|
|
||||||
#include "git.h"
|
#include "git.h"
|
||||||
|
|
||||||
namespace funC {
|
namespace funC {
|
||||||
|
|
||||||
int verbosity, indent, opt_level = 2;
|
int verbosity, indent, opt_level = 2;
|
||||||
bool stack_layout_comments, op_rewrite_comments, program_envelope, asm_preamble;
|
bool stack_layout_comments, op_rewrite_comments, program_envelope, asm_preamble;
|
||||||
std::ostream* outs = &std::cout;
|
bool interactive = false;
|
||||||
std::string generated_from, boc_output_filename;
|
std::string generated_from, boc_output_filename;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -46,58 +44,59 @@ std::string generated_from, boc_output_filename;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void generate_output_func(SymDef* func_sym) {
|
void generate_output_func(SymDef* func_sym, std::ostream &outs, std::ostream &errs) {
|
||||||
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
|
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
|
||||||
assert(func_val);
|
assert(func_val);
|
||||||
std::string name = sym::symbols.get_name(func_sym->sym_idx);
|
std::string name = sym::symbols.get_name(func_sym->sym_idx);
|
||||||
if (verbosity >= 2) {
|
if (verbosity >= 2) {
|
||||||
std::cerr << "\n\n=========================\nfunction " << name << " : " << func_val->get_type() << std::endl;
|
errs << "\n\n=========================\nfunction " << name << " : " << func_val->get_type() << std::endl;
|
||||||
}
|
}
|
||||||
if (!func_val->code) {
|
if (!func_val->code) {
|
||||||
std::cerr << "( function `" << name << "` undefined )\n";
|
errs << "( function `" << name << "` undefined )\n";
|
||||||
|
throw src::ParseError(func_sym->loc, name);
|
||||||
} else {
|
} else {
|
||||||
CodeBlob& code = *(func_val->code);
|
CodeBlob& code = *(func_val->code);
|
||||||
if (verbosity >= 3) {
|
if (verbosity >= 3) {
|
||||||
code.print(std::cerr, 9);
|
code.print(errs, 9);
|
||||||
}
|
}
|
||||||
code.simplify_var_types();
|
code.simplify_var_types();
|
||||||
if (verbosity >= 5) {
|
if (verbosity >= 5) {
|
||||||
std::cerr << "after simplify_var_types: \n";
|
errs << "after simplify_var_types: \n";
|
||||||
code.print(std::cerr, 0);
|
code.print(errs, 0);
|
||||||
}
|
}
|
||||||
code.prune_unreachable_code();
|
code.prune_unreachable_code();
|
||||||
if (verbosity >= 5) {
|
if (verbosity >= 5) {
|
||||||
std::cerr << "after prune_unreachable: \n";
|
errs << "after prune_unreachable: \n";
|
||||||
code.print(std::cerr, 0);
|
code.print(errs, 0);
|
||||||
}
|
}
|
||||||
code.split_vars(true);
|
code.split_vars(true);
|
||||||
if (verbosity >= 5) {
|
if (verbosity >= 5) {
|
||||||
std::cerr << "after split_vars: \n";
|
errs << "after split_vars: \n";
|
||||||
code.print(std::cerr, 0);
|
code.print(errs, 0);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
code.compute_used_code_vars();
|
code.compute_used_code_vars();
|
||||||
if (verbosity >= 4) {
|
if (verbosity >= 4) {
|
||||||
std::cerr << "after compute_used_vars: \n";
|
errs << "after compute_used_vars: \n";
|
||||||
code.print(std::cerr, 6);
|
code.print(errs, 6);
|
||||||
}
|
}
|
||||||
code.fwd_analyze();
|
code.fwd_analyze();
|
||||||
if (verbosity >= 5) {
|
if (verbosity >= 5) {
|
||||||
std::cerr << "after fwd_analyze: \n";
|
errs << "after fwd_analyze: \n";
|
||||||
code.print(std::cerr, 6);
|
code.print(errs, 6);
|
||||||
}
|
}
|
||||||
code.prune_unreachable_code();
|
code.prune_unreachable_code();
|
||||||
if (verbosity >= 5) {
|
if (verbosity >= 5) {
|
||||||
std::cerr << "after prune_unreachable: \n";
|
errs << "after prune_unreachable: \n";
|
||||||
code.print(std::cerr, 6);
|
code.print(errs, 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
code.mark_noreturn();
|
code.mark_noreturn();
|
||||||
if (verbosity >= 3) {
|
if (verbosity >= 3) {
|
||||||
code.print(std::cerr, 15);
|
code.print(errs, 15);
|
||||||
}
|
}
|
||||||
if (verbosity >= 2) {
|
if (verbosity >= 2) {
|
||||||
std::cerr << "\n---------- resulting code for " << name << " -------------\n";
|
errs << "\n---------- resulting code for " << name << " -------------\n";
|
||||||
}
|
}
|
||||||
bool inline_func = (func_val->flags & 1);
|
bool inline_func = (func_val->flags & 1);
|
||||||
bool inline_ref = (func_val->flags & 2);
|
bool inline_ref = (func_val->flags & 2);
|
||||||
|
@ -107,7 +106,7 @@ void generate_output_func(SymDef* func_sym) {
|
||||||
} else if (inline_ref) {
|
} else if (inline_ref) {
|
||||||
modifier = "REF";
|
modifier = "REF";
|
||||||
}
|
}
|
||||||
*outs << std::string(indent * 2, ' ') << name << " PROC" << modifier << ":<{\n";
|
outs << std::string(indent * 2, ' ') << name << " PROC" << modifier << ":<{\n";
|
||||||
int mode = 0;
|
int mode = 0;
|
||||||
if (stack_layout_comments) {
|
if (stack_layout_comments) {
|
||||||
mode |= Stack::_StkCmt | Stack::_CptStkCmt;
|
mode |= Stack::_StkCmt | Stack::_CptStkCmt;
|
||||||
|
@ -120,145 +119,71 @@ void generate_output_func(SymDef* func_sym) {
|
||||||
if (fv && (fv->flags & 1) && code.ops->noreturn()) {
|
if (fv && (fv->flags & 1) && code.ops->noreturn()) {
|
||||||
mode |= Stack::_InlineFunc;
|
mode |= Stack::_InlineFunc;
|
||||||
}
|
}
|
||||||
code.generate_code(*outs, mode, indent + 1);
|
code.generate_code(outs, mode, indent + 1);
|
||||||
*outs << std::string(indent * 2, ' ') << "}>\n";
|
outs << std::string(indent * 2, ' ') << "}>\n";
|
||||||
if (verbosity >= 2) {
|
if (verbosity >= 2) {
|
||||||
std::cerr << "--------------\n";
|
errs << "--------------\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int generate_output() {
|
int generate_output(std::ostream &outs, std::ostream &errs) {
|
||||||
if (asm_preamble) {
|
if (asm_preamble) {
|
||||||
*outs << "\"Asm.fif\" include\n";
|
outs << "\"Asm.fif\" include\n";
|
||||||
}
|
}
|
||||||
*outs << "// automatically generated from " << generated_from << std::endl;
|
outs << "// automatically generated from " << generated_from << std::endl;
|
||||||
if (program_envelope) {
|
if (program_envelope) {
|
||||||
*outs << "PROGRAM{\n";
|
outs << "PROGRAM{\n";
|
||||||
}
|
}
|
||||||
for (SymDef* func_sym : glob_func) {
|
for (SymDef* func_sym : glob_func) {
|
||||||
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
|
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
|
||||||
assert(func_val);
|
assert(func_val);
|
||||||
std::string name = sym::symbols.get_name(func_sym->sym_idx);
|
std::string name = sym::symbols.get_name(func_sym->sym_idx);
|
||||||
*outs << std::string(indent * 2, ' ');
|
outs << std::string(indent * 2, ' ');
|
||||||
if (func_val->method_id.is_null()) {
|
if (func_val->method_id.is_null()) {
|
||||||
*outs << "DECLPROC " << name << "\n";
|
outs << "DECLPROC " << name << "\n";
|
||||||
} else {
|
} else {
|
||||||
*outs << func_val->method_id << " DECLMETHOD " << name << "\n";
|
outs << func_val->method_id << " DECLMETHOD " << name << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (SymDef* gvar_sym : glob_vars) {
|
for (SymDef* gvar_sym : glob_vars) {
|
||||||
assert(dynamic_cast<SymValGlobVar*>(gvar_sym->value));
|
assert(dynamic_cast<SymValGlobVar*>(gvar_sym->value));
|
||||||
std::string name = sym::symbols.get_name(gvar_sym->sym_idx);
|
std::string name = sym::symbols.get_name(gvar_sym->sym_idx);
|
||||||
*outs << std::string(indent * 2, ' ') << "DECLGLOBVAR " << name << "\n";
|
outs << std::string(indent * 2, ' ') << "DECLGLOBVAR " << name << "\n";
|
||||||
}
|
}
|
||||||
int errors = 0;
|
int errors = 0;
|
||||||
for (SymDef* func_sym : glob_func) {
|
for (SymDef* func_sym : glob_func) {
|
||||||
try {
|
try {
|
||||||
generate_output_func(func_sym);
|
generate_output_func(func_sym, outs, errs);
|
||||||
} catch (src::Error& err) {
|
} catch (src::Error& err) {
|
||||||
std::cerr << "cannot generate code for function `" << sym::symbols.get_name(func_sym->sym_idx) << "`:\n"
|
errs << "cannot generate code for function `" << sym::symbols.get_name(func_sym->sym_idx) << "`:\n"
|
||||||
<< err << std::endl;
|
<< err << std::endl;
|
||||||
++errors;
|
++errors;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (program_envelope) {
|
if (program_envelope) {
|
||||||
*outs << "}END>c\n";
|
outs << "}END>c\n";
|
||||||
}
|
}
|
||||||
if (!boc_output_filename.empty()) {
|
if (!boc_output_filename.empty()) {
|
||||||
*outs << "2 boc+>B \"" << boc_output_filename << "\" B>file\n";
|
outs << "2 boc+>B \"" << boc_output_filename << "\" B>file\n";
|
||||||
}
|
}
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace funC
|
void output_inclusion_stack(std::ostream &errs) {
|
||||||
|
|
||||||
void usage(const char* progname) {
|
|
||||||
std::cerr
|
|
||||||
<< "usage: " << progname
|
|
||||||
<< " [-vIAPSR][-O<level>][-i<indent-spc>][-o<output-filename>][-W<boc-filename>] {<func-source-filename> ...}\n"
|
|
||||||
"\tGenerates Fift TVM assembler code from a funC source\n"
|
|
||||||
"-I\tEnables interactive mode (parse stdin)\n"
|
|
||||||
"-o<fift-output-filename>\tWrites generated code into specified file instead of stdout\n"
|
|
||||||
"-v\tIncreases verbosity level (extra information output into stderr)\n"
|
|
||||||
"-i<indent>\tSets indentation for the output code (in two-space units)\n"
|
|
||||||
"-A\tPrefix code with `\"Asm.fif\" include` preamble\n"
|
|
||||||
"-O<level>\tSets optimization level (2 by default)\n"
|
|
||||||
"-P\tEnvelope code into PROGRAM{ ... }END>c\n"
|
|
||||||
"-S\tInclude stack layout comments in the output code\n"
|
|
||||||
"-R\tInclude operation rewrite comments in the output code\n"
|
|
||||||
"-W<output-boc-file>\tInclude Fift code to serialize and save generated code into specified BoC file. Enables "
|
|
||||||
"-A and -P.\n"
|
|
||||||
"\t-s\tOutput semantic version of FunC and exit\n"
|
|
||||||
"\t-V<version>\tShow func build information\n";
|
|
||||||
std::exit(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void output_inclusion_stack() {
|
|
||||||
while (!funC::inclusion_locations.empty()) {
|
while (!funC::inclusion_locations.empty()) {
|
||||||
src::SrcLocation loc = funC::inclusion_locations.top();
|
src::SrcLocation loc = funC::inclusion_locations.top();
|
||||||
funC::inclusion_locations.pop();
|
funC::inclusion_locations.pop();
|
||||||
if (loc.fdescr) {
|
if (loc.fdescr) {
|
||||||
std::cerr << "note: included from ";
|
errs << "note: included from ";
|
||||||
loc.show(std::cerr);
|
loc.show(errs);
|
||||||
std::cerr << std::endl;
|
errs << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string output_filename;
|
|
||||||
|
|
||||||
int main(int argc, char* const argv[]) {
|
|
||||||
int i;
|
|
||||||
bool interactive = false;
|
|
||||||
while ((i = getopt(argc, argv, "Ahi:Io:O:PRsSvW:V")) != -1) {
|
|
||||||
switch (i) {
|
|
||||||
case 'A':
|
|
||||||
funC::asm_preamble = true;
|
|
||||||
break;
|
|
||||||
case 'I':
|
|
||||||
interactive = true;
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
funC::indent = std::max(0, atoi(optarg));
|
|
||||||
break;
|
|
||||||
case 'o':
|
|
||||||
output_filename = optarg;
|
|
||||||
break;
|
|
||||||
case 'O':
|
|
||||||
funC::opt_level = std::max(0, atoi(optarg));
|
|
||||||
break;
|
|
||||||
case 'P':
|
|
||||||
funC::program_envelope = true;
|
|
||||||
break;
|
|
||||||
case 'R':
|
|
||||||
funC::op_rewrite_comments = true;
|
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
funC::stack_layout_comments = true;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
++funC::verbosity;
|
|
||||||
break;
|
|
||||||
case 'W':
|
|
||||||
funC::boc_output_filename = optarg;
|
|
||||||
funC::asm_preamble = funC::program_envelope = true;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
std::cout << funC::func_version << "\n";
|
|
||||||
std::exit(0);
|
|
||||||
break;
|
|
||||||
case 'V':
|
|
||||||
std::cout << "FunC semantic version: v" << funC::func_version << "\n";
|
|
||||||
std::cout << "Build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n";
|
|
||||||
std::exit(0);
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
default:
|
|
||||||
usage(argv[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
int func_proceed(const std::vector<std::string> &sources, std::ostream &outs, std::ostream &errs) {
|
||||||
if (funC::program_envelope && !funC::indent) {
|
if (funC::program_envelope && !funC::indent) {
|
||||||
funC::indent = 1;
|
funC::indent = 1;
|
||||||
}
|
}
|
||||||
|
@ -268,12 +193,11 @@ int main(int argc, char* const argv[]) {
|
||||||
|
|
||||||
int ok = 0, proc = 0;
|
int ok = 0, proc = 0;
|
||||||
try {
|
try {
|
||||||
while (optind < argc) {
|
for (auto src : sources) {
|
||||||
// funC::generated_from += std::string{"`"} + argv[optind] + "` ";
|
ok += funC::parse_source_file(src.c_str());
|
||||||
ok += funC::parse_source_file(argv[optind++]);
|
|
||||||
proc++;
|
proc++;
|
||||||
}
|
}
|
||||||
if (interactive) {
|
if (funC::interactive) {
|
||||||
funC::generated_from += "stdin ";
|
funC::generated_from += "stdin ";
|
||||||
ok += funC::parse_source_stdin();
|
ok += funC::parse_source_stdin();
|
||||||
proc++;
|
proc++;
|
||||||
|
@ -284,29 +208,24 @@ int main(int argc, char* const argv[]) {
|
||||||
if (!proc) {
|
if (!proc) {
|
||||||
throw src::Fatal{"no source files, no output"};
|
throw src::Fatal{"no source files, no output"};
|
||||||
}
|
}
|
||||||
std::unique_ptr<std::fstream> fs;
|
return funC::generate_output(outs, errs);
|
||||||
if (!output_filename.empty()) {
|
} catch (src::Fatal& fatal) {
|
||||||
fs = std::make_unique<std::fstream>(output_filename, fs->trunc | fs->out);
|
errs << "fatal: " << fatal << std::endl;
|
||||||
if (!fs->is_open()) {
|
output_inclusion_stack(errs);
|
||||||
std::cerr << "failed to create output file " << output_filename << '\n';
|
return 2;
|
||||||
|
} catch (src::Error& error) {
|
||||||
|
errs << error << std::endl;
|
||||||
|
output_inclusion_stack(errs);
|
||||||
|
return 2;
|
||||||
|
} catch (funC::UnifyError& unif_err) {
|
||||||
|
errs << "fatal: ";
|
||||||
|
unif_err.print_message(errs);
|
||||||
|
errs << std::endl;
|
||||||
|
output_inclusion_stack(errs);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
funC::outs = fs.get();
|
|
||||||
}
|
return 0;
|
||||||
funC::generate_output();
|
|
||||||
} catch (src::Fatal& fatal) {
|
|
||||||
std::cerr << "fatal: " << fatal << std::endl;
|
|
||||||
output_inclusion_stack();
|
|
||||||
std::exit(1);
|
|
||||||
} catch (src::Error& error) {
|
|
||||||
std::cerr << error << std::endl;
|
|
||||||
output_inclusion_stack();
|
|
||||||
std::exit(1);
|
|
||||||
} catch (funC::UnifyError& unif_err) {
|
|
||||||
std::cerr << "fatal: ";
|
|
||||||
unif_err.print_message(std::cerr);
|
|
||||||
std::cerr << std::endl;
|
|
||||||
output_inclusion_stack();
|
|
||||||
std::exit(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace funC
|
|
@ -39,7 +39,7 @@ extern std::string generated_from;
|
||||||
|
|
||||||
constexpr int optimize_depth = 20;
|
constexpr int optimize_depth = 20;
|
||||||
|
|
||||||
const std::string func_version{"0.2.0"};
|
const std::string func_version{"0.3.0"};
|
||||||
|
|
||||||
enum Keyword {
|
enum Keyword {
|
||||||
_Eof = -1,
|
_Eof = -1,
|
||||||
|
@ -1631,6 +1631,7 @@ inline compile_func_t make_ext_compile(AsmOp op) {
|
||||||
struct SymValAsmFunc : SymValFunc {
|
struct SymValAsmFunc : SymValFunc {
|
||||||
simple_compile_func_t simple_compile;
|
simple_compile_func_t simple_compile;
|
||||||
compile_func_t ext_compile;
|
compile_func_t ext_compile;
|
||||||
|
td::uint64 crc;
|
||||||
~SymValAsmFunc() override = default;
|
~SymValAsmFunc() override = default;
|
||||||
SymValAsmFunc(TypeExpr* ft, const AsmOp& _macro, bool impure = false)
|
SymValAsmFunc(TypeExpr* ft, const AsmOp& _macro, bool impure = false)
|
||||||
: SymValFunc(-1, ft, impure), simple_compile(make_simple_compile(_macro)) {
|
: SymValFunc(-1, ft, impure), simple_compile(make_simple_compile(_macro)) {
|
||||||
|
@ -1665,4 +1666,19 @@ AsmOp push_const(td::RefInt256 x);
|
||||||
|
|
||||||
void define_builtins();
|
void define_builtins();
|
||||||
|
|
||||||
|
|
||||||
|
extern int verbosity, indent, opt_level;
|
||||||
|
extern bool stack_layout_comments, op_rewrite_comments, program_envelope, asm_preamble, interactive;
|
||||||
|
extern std::string generated_from, boc_output_filename;
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* OUTPUT CODE GENERATOR
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
int func_proceed(const std::vector<std::string> &sources, std::ostream &outs, std::ostream &errs);
|
||||||
|
|
||||||
} // namespace funC
|
} // namespace funC
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -254,9 +254,7 @@ void parse_const_decl(Lexer& lex) {
|
||||||
if (!sym_def) {
|
if (!sym_def) {
|
||||||
lex.cur().error_at("cannot define global symbol `", "`");
|
lex.cur().error_at("cannot define global symbol `", "`");
|
||||||
}
|
}
|
||||||
if (sym_def->value) {
|
Lexem ident = lex.cur();
|
||||||
lex.cur().error_at("global symbol `", "` already exists");
|
|
||||||
}
|
|
||||||
lex.next();
|
lex.next();
|
||||||
if (lex.tp() != '=') {
|
if (lex.tp() != '=') {
|
||||||
lex.cur().error_at("expected = instead of ", "");
|
lex.cur().error_at("expected = instead of ", "");
|
||||||
|
@ -273,10 +271,11 @@ void parse_const_decl(Lexer& lex) {
|
||||||
if ((wanted_type != Expr::_None) && (x->cls != wanted_type)) {
|
if ((wanted_type != Expr::_None) && (x->cls != wanted_type)) {
|
||||||
lex.cur().error("expression type does not match wanted type");
|
lex.cur().error("expression type does not match wanted type");
|
||||||
}
|
}
|
||||||
|
SymValConst* new_value = nullptr;
|
||||||
if (x->cls == Expr::_Const) { // Integer constant
|
if (x->cls == Expr::_Const) { // Integer constant
|
||||||
sym_def->value = new SymValConst{const_cnt++, x->intval};
|
new_value = new SymValConst{const_cnt++, x->intval};
|
||||||
} else if (x->cls == Expr::_SliceConst) { // Slice constant (string)
|
} else if (x->cls == Expr::_SliceConst) { // Slice constant (string)
|
||||||
sym_def->value = new SymValConst{const_cnt++, x->strval};
|
new_value = new SymValConst{const_cnt++, x->strval};
|
||||||
} else if (x->cls == Expr::_Apply) {
|
} else if (x->cls == Expr::_Apply) {
|
||||||
code.emplace_back(loc, Op::_Import, std::vector<var_idx_t>());
|
code.emplace_back(loc, Op::_Import, std::vector<var_idx_t>());
|
||||||
auto tmp_vars = x->pre_compile(code);
|
auto tmp_vars = x->pre_compile(code);
|
||||||
|
@ -304,10 +303,20 @@ void parse_const_decl(Lexer& lex) {
|
||||||
if (op.origin.is_null() || !op.origin->is_valid()) {
|
if (op.origin.is_null() || !op.origin->is_valid()) {
|
||||||
lex.cur().error("precompiled expression did not result in a valid integer constant");
|
lex.cur().error("precompiled expression did not result in a valid integer constant");
|
||||||
}
|
}
|
||||||
sym_def->value = new SymValConst{const_cnt++, op.origin};
|
new_value = new SymValConst{const_cnt++, op.origin};
|
||||||
} else {
|
} else {
|
||||||
lex.cur().error("integer or slice literal or constant expected");
|
lex.cur().error("integer or slice literal or constant expected");
|
||||||
}
|
}
|
||||||
|
if (sym_def->value) {
|
||||||
|
SymValConst* old_value = dynamic_cast<SymValConst*>(sym_def->value);
|
||||||
|
Keyword new_type = new_value->get_type();
|
||||||
|
if (!old_value || old_value->get_type() != new_type ||
|
||||||
|
(new_type == _Int && *old_value->get_int_value() != *new_value->get_int_value()) ||
|
||||||
|
(new_type == _Slice && old_value->get_str_value() != new_value->get_str_value())) {
|
||||||
|
ident.error_at("global symbol `", "` already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sym_def->value = new_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
FormalArgList parse_formal_args(Lexer& lex) {
|
FormalArgList parse_formal_args(Lexer& lex) {
|
||||||
|
@ -1261,19 +1270,48 @@ SymValAsmFunc* parse_asm_func_body(Lexer& lex, TypeExpr* func_type, const Formal
|
||||||
lex.expect(')');
|
lex.expect(')');
|
||||||
}
|
}
|
||||||
while (lex.tp() == _String) {
|
while (lex.tp() == _String) {
|
||||||
asm_ops.push_back(AsmOp::Parse(lex.cur().str, cnt, width));
|
std::string ops = lex.cur().str; // <op>\n<op>\n...
|
||||||
lex.next();
|
std::string op;
|
||||||
|
for (const char& c : ops) {
|
||||||
|
if (c == '\n') {
|
||||||
|
if (!op.empty()) {
|
||||||
|
asm_ops.push_back(AsmOp::Parse(op, cnt, width));
|
||||||
if (asm_ops.back().is_custom()) {
|
if (asm_ops.back().is_custom()) {
|
||||||
cnt = width;
|
cnt = width;
|
||||||
}
|
}
|
||||||
|
op.clear();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
op.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!op.empty()) {
|
||||||
|
asm_ops.push_back(AsmOp::Parse(op, cnt, width));
|
||||||
|
if (asm_ops.back().is_custom()) {
|
||||||
|
cnt = width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lex.next();
|
||||||
}
|
}
|
||||||
if (asm_ops.empty()) {
|
if (asm_ops.empty()) {
|
||||||
throw src::ParseError{lex.cur().loc, "string with assembler instruction expected"};
|
throw src::ParseError{lex.cur().loc, "string with assembler instruction expected"};
|
||||||
}
|
}
|
||||||
lex.expect(';');
|
lex.expect(';');
|
||||||
|
std::string crc_s;
|
||||||
|
for (const AsmOp& asm_op : asm_ops) {
|
||||||
|
crc_s += asm_op.op;
|
||||||
|
}
|
||||||
|
crc_s.push_back(impure);
|
||||||
|
for (const int& x : arg_order) {
|
||||||
|
crc_s += std::string((const char*) (&x), (const char*) (&x + 1));
|
||||||
|
}
|
||||||
|
for (const int& x : ret_order) {
|
||||||
|
crc_s += std::string((const char*) (&x), (const char*) (&x + 1));
|
||||||
|
}
|
||||||
auto res = new SymValAsmFunc{func_type, asm_ops, impure};
|
auto res = new SymValAsmFunc{func_type, asm_ops, impure};
|
||||||
res->arg_order = std::move(arg_order);
|
res->arg_order = std::move(arg_order);
|
||||||
res->ret_order = std::move(ret_order);
|
res->ret_order = std::move(ret_order);
|
||||||
|
res->crc = td::crc64(crc_s);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1439,16 +1477,22 @@ void parse_func_def(Lexer& lex) {
|
||||||
// code->print(std::cerr); // !!!DEBUG!!!
|
// code->print(std::cerr); // !!!DEBUG!!!
|
||||||
func_sym_code->code = code;
|
func_sym_code->code = code;
|
||||||
} else {
|
} else {
|
||||||
|
Lexem asm_lexem = lex.cur();
|
||||||
|
SymValAsmFunc* asm_func = parse_asm_func_body(lex, func_type, arg_list, ret_type, impure);
|
||||||
if (func_sym_val) {
|
if (func_sym_val) {
|
||||||
if (dynamic_cast<SymValCodeFunc*>(func_sym_val)) {
|
if (dynamic_cast<SymValCodeFunc*>(func_sym_val)) {
|
||||||
lex.cur().error("function `"s + func_name.str + "` was already declared as an ordinary function");
|
asm_lexem.error("function `"s + func_name.str + "` was already declared as an ordinary function");
|
||||||
}
|
}
|
||||||
if (dynamic_cast<SymValAsmFunc*>(func_sym_val)) {
|
SymValAsmFunc* asm_func_old = dynamic_cast<SymValAsmFunc*>(func_sym_val);
|
||||||
lex.cur().error("redefinition of built-in assembler function `"s + func_name.str + "`");
|
if (asm_func_old) {
|
||||||
|
if (asm_func->crc != asm_func_old->crc) {
|
||||||
|
asm_lexem.error("redefinition of built-in assembler function `"s + func_name.str + "`");
|
||||||
}
|
}
|
||||||
lex.cur().error("redefinition of previously (somehow) defined function `"s + func_name.str + "`");
|
} else {
|
||||||
|
asm_lexem.error("redefinition of previously (somehow) defined function `"s + func_name.str + "`");
|
||||||
}
|
}
|
||||||
func_sym->value = parse_asm_func_body(lex, func_type, arg_list, ret_type, impure);
|
}
|
||||||
|
func_sym->value = asm_func;
|
||||||
}
|
}
|
||||||
if (method_id.not_null()) {
|
if (method_id.not_null()) {
|
||||||
auto val = dynamic_cast<SymVal*>(func_sym->value);
|
auto val = dynamic_cast<SymVal*>(func_sym->value);
|
||||||
|
@ -1657,7 +1701,14 @@ bool parse_source_file(const char* filename, src::Lexem lex) {
|
||||||
throw src::Fatal{msg};
|
throw src::Fatal{msg};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::string real_filename = td::realpath(td::CSlice(filename)).move_as_ok();
|
|
||||||
|
auto path_res = td::realpath(td::CSlice(filename));
|
||||||
|
if (path_res.is_error()) {
|
||||||
|
auto error = path_res.move_as_error();
|
||||||
|
lex.error(error.message().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string real_filename = path_res.move_as_ok();
|
||||||
if (std::count(source_files.begin(), source_files.end(), real_filename)) {
|
if (std::count(source_files.begin(), source_files.end(), real_filename)) {
|
||||||
if (verbosity >= 2) {
|
if (verbosity >= 2) {
|
||||||
if (lex.tp) {
|
if (lex.tp) {
|
||||||
|
|
15
crypto/func/test/co2.fc
Normal file
15
crypto/func/test/co2.fc
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
const int x = 5;
|
||||||
|
const slice s = "abacaba";
|
||||||
|
const int y = 3;
|
||||||
|
const slice s = "abacaba";
|
||||||
|
const int x = 5;
|
||||||
|
const int z = 4, z = 4;
|
||||||
|
|
||||||
|
int sdeq (slice s1, slice s2) asm "SDEQ";
|
||||||
|
|
||||||
|
() main() {
|
||||||
|
throw_unless(101, x == 5);
|
||||||
|
throw_unless(102, y == 3);
|
||||||
|
throw_unless(103, z == 4);
|
||||||
|
throw_unless(104, sdeq(s, "abacaba"));
|
||||||
|
}
|
24
crypto/func/test/co3.fc
Normal file
24
crypto/func/test/co3.fc
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
const val1 = 123456789;
|
||||||
|
const val2 = 987654321;
|
||||||
|
const val3 = 135792468;
|
||||||
|
const val4 = 246813579;
|
||||||
|
|
||||||
|
const prec_and = val1 & val2;
|
||||||
|
const prec_or = val1 | val2;
|
||||||
|
const prec_xor = val1 ^ val2;
|
||||||
|
const prec_logic = ((val1 & val2) | val3) ^ val4;
|
||||||
|
const prec_nand = val1 & (~ val2);
|
||||||
|
|
||||||
|
int get_and() { return prec_and; }
|
||||||
|
int get_or() { return prec_or; }
|
||||||
|
int get_xor() { return prec_xor; }
|
||||||
|
int get_logic() { return prec_logic; }
|
||||||
|
int get_nand() { return prec_nand; }
|
||||||
|
|
||||||
|
_ main() {
|
||||||
|
throw_unless(101, get_and() == 39471121);
|
||||||
|
throw_unless(102, get_or() == 1071639989);
|
||||||
|
throw_unless(103, get_xor() == 1032168868);
|
||||||
|
throw_unless(104, get_logic() == 82599134);
|
||||||
|
throw_unless(105, get_nand() == 83985668);
|
||||||
|
}
|
26
crypto/func/test/s2.fc
Normal file
26
crypto/func/test/s2.fc
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
slice test1() asm """
|
||||||
|
"Test" $>s
|
||||||
|
PUSHSLICE
|
||||||
|
""";
|
||||||
|
|
||||||
|
slice test2() asm """
|
||||||
|
"Hello"
|
||||||
|
" "
|
||||||
|
"World"
|
||||||
|
$+ $+ $>s
|
||||||
|
PUSHSLICE
|
||||||
|
""";
|
||||||
|
|
||||||
|
int sdeq (slice s1, slice s2) asm """SDEQ""";
|
||||||
|
int sdeq (slice s1, slice s2) asm "SDEQ" "";
|
||||||
|
int sdeq (slice s1, slice s2) asm "" """
|
||||||
|
SDEQ
|
||||||
|
""";
|
||||||
|
|
||||||
|
() main() {
|
||||||
|
slice s = test1();
|
||||||
|
throw_unless(101, sdeq(s, "Test"));
|
||||||
|
|
||||||
|
slice s = test2();
|
||||||
|
throw_unless(102, sdeq(s, "Hello World"));
|
||||||
|
}
|
131
crypto/funcfiftlib/funcfiftlib.cpp
Normal file
131
crypto/funcfiftlib/funcfiftlib.cpp
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
This file is part of TON Blockchain source code.
|
||||||
|
|
||||||
|
TON Blockchain is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU 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 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
#include "func/func.h"
|
||||||
|
#include "git.h"
|
||||||
|
#include "td/utils/JsonBuilder.h"
|
||||||
|
#include "fift/utils.h"
|
||||||
|
#include "td/utils/base64.h"
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
std::string escape_json(const std::string &s) {
|
||||||
|
std::ostringstream o;
|
||||||
|
for (auto c = s.cbegin(); c != s.cend(); c++) {
|
||||||
|
switch (*c) {
|
||||||
|
case '"': o << "\\\""; break;
|
||||||
|
case '\\': o << "\\\\"; break;
|
||||||
|
case '\b': o << "\\b"; break;
|
||||||
|
case '\f': o << "\\f"; break;
|
||||||
|
case '\n': o << "\\n"; break;
|
||||||
|
case '\r': o << "\\r"; break;
|
||||||
|
case '\t': o << "\\t"; break;
|
||||||
|
default:
|
||||||
|
if ('\x00' <= *c && *c <= '\x1f') {
|
||||||
|
o << "\\u"
|
||||||
|
<< std::hex << std::setw(4) << std::setfill('0') << static_cast<int>(*c);
|
||||||
|
} else {
|
||||||
|
o << *c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return o.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
td::Result<std::string> compile_internal(char *config_json) {
|
||||||
|
TRY_RESULT(input_json, td::json_decode(td::MutableSlice(config_json)))
|
||||||
|
auto &obj = input_json.get_object();
|
||||||
|
|
||||||
|
TRY_RESULT(opt_level, td::get_json_object_int_field(obj, "optLevel", false));
|
||||||
|
TRY_RESULT(sources_obj, td::get_json_object_field(obj, "sources", td::JsonValue::Type::Array, false));
|
||||||
|
|
||||||
|
auto &sources_arr = sources_obj.get_array();
|
||||||
|
|
||||||
|
std::vector<std::string> sources;
|
||||||
|
|
||||||
|
for (auto &item : sources_arr) {
|
||||||
|
sources.push_back(item.get_string().str());
|
||||||
|
}
|
||||||
|
|
||||||
|
funC::opt_level = std::max(0, opt_level);
|
||||||
|
funC::program_envelope = true;
|
||||||
|
funC::verbosity = 0;
|
||||||
|
funC::indent = 1;
|
||||||
|
|
||||||
|
std::ostringstream outs, errs;
|
||||||
|
auto compile_res = funC::func_proceed(sources, outs, errs);
|
||||||
|
|
||||||
|
if (compile_res != 0) {
|
||||||
|
return td::Status::Error(std::string("Func compilation error: ") + errs.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
TRY_RESULT(code_cell, fift::compile_asm(outs.str(), "/fiftlib/", false));
|
||||||
|
TRY_RESULT(boc, vm::std_boc_serialize(code_cell));
|
||||||
|
|
||||||
|
td::JsonBuilder result_json;
|
||||||
|
auto result_obj = result_json.enter_object();
|
||||||
|
result_obj("status", "ok");
|
||||||
|
result_obj("codeBoc", td::base64_encode(boc));
|
||||||
|
result_obj("fiftCode", escape_json(outs.str()));
|
||||||
|
result_obj.leave();
|
||||||
|
|
||||||
|
outs.clear();
|
||||||
|
errs.clear();
|
||||||
|
|
||||||
|
return result_json.string_builder().as_cslice().str();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
const char* version() {
|
||||||
|
auto version_json = td::JsonBuilder();
|
||||||
|
auto obj = version_json.enter_object();
|
||||||
|
obj("funcVersion", funC::func_version);
|
||||||
|
obj("funcFiftLibCommitHash", GitMetadata::CommitSHA1());
|
||||||
|
obj("funcFiftLibCommitDate", GitMetadata::CommitDate());
|
||||||
|
obj.leave();
|
||||||
|
return strdup(version_json.string_builder().as_cslice().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *func_compile(char *config_json) {
|
||||||
|
auto res = compile_internal(config_json);
|
||||||
|
|
||||||
|
if (res.is_error()) {
|
||||||
|
auto result = res.move_as_error();
|
||||||
|
auto error_res = td::JsonBuilder();
|
||||||
|
auto error_o = error_res.enter_object();
|
||||||
|
error_o("status", "error");
|
||||||
|
error_o("message", result.message().str());
|
||||||
|
error_o.leave();
|
||||||
|
return strdup(error_res.string_builder().as_cslice().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res_string = res.move_as_ok();
|
||||||
|
|
||||||
|
return strdup(res_string.c_str());
|
||||||
|
}
|
||||||
|
}
|
|
@ -125,8 +125,9 @@ int Lexem::set(std::string _str, const SrcLocation& _loc, int _tp, int _val) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Lexer::Lexer(SourceReader& _src, bool init, std::string active_chars, std::string eol_cmts, std::string open_cmts,
|
Lexer::Lexer(SourceReader& _src, bool init, std::string active_chars, std::string eol_cmts, std::string open_cmts,
|
||||||
std::string close_cmts, std::string quote_chars)
|
std::string close_cmts, std::string quote_chars, std::string multiline_quote)
|
||||||
: src(_src), eof(false), lexem("", src.here(), Lexem::Undefined), peek_lexem("", {}, Lexem::Undefined) {
|
: src(_src), eof(false), lexem("", src.here(), Lexem::Undefined), peek_lexem("", {}, Lexem::Undefined),
|
||||||
|
multiline_quote(std::move(multiline_quote)) {
|
||||||
std::memset(char_class, 0, sizeof(char_class));
|
std::memset(char_class, 0, sizeof(char_class));
|
||||||
unsigned char activity = cc::active;
|
unsigned char activity = cc::active;
|
||||||
for (char c : active_chars) {
|
for (char c : active_chars) {
|
||||||
|
@ -171,6 +172,19 @@ void Lexer::set_spec(std::array<int, 3>& arr, std::string setup) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Lexer::is_multiline_quote(const char* begin, const char* end) {
|
||||||
|
if (multiline_quote.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const char& c : multiline_quote) {
|
||||||
|
if (begin == end || *begin != c) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++begin;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Lexer::expect(int exp_tp, const char* msg) {
|
void Lexer::expect(int exp_tp, const char* msg) {
|
||||||
if (tp() != exp_tp) {
|
if (tp() != exp_tp) {
|
||||||
throw ParseError{lexem.loc, (msg ? std::string{msg} : Lexem::lexem_name_str(exp_tp)) + " expected instead of " +
|
throw ParseError{lexem.loc, (msg ? std::string{msg} : Lexem::lexem_name_str(exp_tp)) + " expected instead of " +
|
||||||
|
@ -234,6 +248,37 @@ const Lexem& Lexer::next() {
|
||||||
}
|
}
|
||||||
return lexem.clear(src.here(), Lexem::Eof);
|
return lexem.clear(src.here(), Lexem::Eof);
|
||||||
}
|
}
|
||||||
|
if (is_multiline_quote(src.get_ptr(), src.get_end_ptr())) {
|
||||||
|
src.advance(multiline_quote.size());
|
||||||
|
const char* begin = src.get_ptr();
|
||||||
|
const char* end = nullptr;
|
||||||
|
SrcLocation here = src.here();
|
||||||
|
std::string body;
|
||||||
|
while (!src.is_eof()) {
|
||||||
|
if (src.is_eoln()) {
|
||||||
|
body.push_back('\n');
|
||||||
|
src.load_line();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (is_multiline_quote(src.get_ptr(), src.get_end_ptr())) {
|
||||||
|
end = src.get_ptr();
|
||||||
|
src.advance(multiline_quote.size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
body.push_back(src.cur_char());
|
||||||
|
src.advance(1);
|
||||||
|
}
|
||||||
|
if (!end) {
|
||||||
|
src.error("string extends past end of file");
|
||||||
|
}
|
||||||
|
lexem.set(body, here, Lexem::String);
|
||||||
|
int c = src.cur_char();
|
||||||
|
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
|
||||||
|
lexem.val = c;
|
||||||
|
src.advance(1);
|
||||||
|
}
|
||||||
|
return lexem;
|
||||||
|
}
|
||||||
int c = src.cur_char();
|
int c = src.cur_char();
|
||||||
const char* end = src.get_ptr();
|
const char* end = src.get_ptr();
|
||||||
if (is_quote_char(c) || c == '`') {
|
if (is_quote_char(c) || c == '`') {
|
||||||
|
|
|
@ -71,6 +71,7 @@ class Lexer {
|
||||||
Lexem lexem, peek_lexem;
|
Lexem lexem, peek_lexem;
|
||||||
unsigned char char_class[128];
|
unsigned char char_class[128];
|
||||||
std::array<int, 3> eol_cmt, cmt_op, cmt_cl;
|
std::array<int, 3> eol_cmt, cmt_op, cmt_cl;
|
||||||
|
std::string multiline_quote;
|
||||||
enum cc { left_active = 2, right_active = 1, active = 3, allow_repeat = 4, quote_char = 8 };
|
enum cc { left_active = 2, right_active = 1, active = 3, allow_repeat = 4, quote_char = 8 };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -78,7 +79,8 @@ class Lexer {
|
||||||
return eof;
|
return eof;
|
||||||
}
|
}
|
||||||
Lexer(SourceReader& _src, bool init = false, std::string active_chars = ";,() ~.", std::string eol_cmts = ";;",
|
Lexer(SourceReader& _src, bool init = false, std::string active_chars = ";,() ~.", std::string eol_cmts = ";;",
|
||||||
std::string open_cmts = "{-", std::string close_cmts = "-}", std::string quote_chars = "\"");
|
std::string open_cmts = "{-", std::string close_cmts = "-}", std::string quote_chars = "\"",
|
||||||
|
std::string multiline_quote = "\"\"\"");
|
||||||
const Lexem& next();
|
const Lexem& next();
|
||||||
const Lexem& cur() const {
|
const Lexem& cur() const {
|
||||||
return lexem;
|
return lexem;
|
||||||
|
@ -109,6 +111,7 @@ class Lexer {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void set_spec(std::array<int, 3>& arr, std::string setup);
|
void set_spec(std::array<int, 3>& arr, std::string setup);
|
||||||
|
bool is_multiline_quote(const char* begin, const char* end);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace src
|
} // namespace src
|
||||||
|
|
|
@ -433,8 +433,12 @@ td::Result<std::vector<ManualDns::RawEntry>> ManualDns::resolve_raw_or_throw(td:
|
||||||
} else {
|
} else {
|
||||||
if (category.is_zero()) {
|
if (category.is_zero()) {
|
||||||
vm::Dictionary dict(std::move(data), 256);
|
vm::Dictionary dict(std::move(data), 256);
|
||||||
dict.check_for_each([&](auto cs, td::ConstBitPtr key, int n) {
|
dict.check_for_each([&](td::Ref<vm::CellSlice> cs, td::ConstBitPtr key, int n) {
|
||||||
CHECK(n == 256);
|
CHECK(n == 256);
|
||||||
|
if (cs.is_null() || cs->size_ext() != 0x10000) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
cs = vm::load_cell_slice_ref(cs->prefetch_ref());
|
||||||
vec.push_back({name.str(), td::Bits256(key), cs});
|
vec.push_back({name.str(), td::Bits256(key), cs});
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
|
@ -2423,7 +2423,7 @@ std::vector<const src::FileDescr*> source_fdescr;
|
||||||
|
|
||||||
bool parse_source(std::istream* is, src::FileDescr* fdescr) {
|
bool parse_source(std::istream* is, src::FileDescr* fdescr) {
|
||||||
src::SourceReader reader{is, fdescr};
|
src::SourceReader reader{is, fdescr};
|
||||||
src::Lexer lex{reader, true, "(){}:;? #$. ^~ #", "//", "/*", "*/"};
|
src::Lexer lex{reader, true, "(){}:;? #$. ^~ #", "//", "/*", "*/", ""};
|
||||||
while (lex.tp() != src::_Eof) {
|
while (lex.tp() != src::_Eof) {
|
||||||
parse_constructor_def(lex);
|
parse_constructor_def(lex);
|
||||||
// std::cerr << lex.cur().str << '\t' << lex.cur().name_str() << std::endl;
|
// std::cerr << lex.cur().str << '\t' << lex.cur().name_str() << std::endl;
|
||||||
|
|
|
@ -125,9 +125,12 @@ bool TupleT::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TLB::validate_ref_internal(int* ops, Ref<vm::Cell> cell_ref, bool weak) const {
|
bool TLB::validate_ref_internal(int* ops, Ref<vm::Cell> cell_ref, bool weak) const {
|
||||||
if (ops && --*ops < 0) {
|
if (ops) {
|
||||||
|
if (*ops <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
--*ops;
|
||||||
|
}
|
||||||
bool is_special;
|
bool is_special;
|
||||||
auto cs = load_cell_slice_special(std::move(cell_ref), 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()));
|
return always_special() ? is_special : (is_special ? weak : (validate_skip(ops, cs) && cs.empty_ext()));
|
||||||
|
|
|
@ -335,7 +335,7 @@ bool CellBuilder::store_ulong_rchk_bool(unsigned long long val, unsigned val_bit
|
||||||
}
|
}
|
||||||
|
|
||||||
CellBuilder& CellBuilder::store_long(long long val, unsigned val_bits) {
|
CellBuilder& CellBuilder::store_long(long long val, unsigned val_bits) {
|
||||||
return store_long_top(val << (64 - val_bits), val_bits);
|
return store_long_top(val_bits == 0 ? 0 : (unsigned long long)val << (64 - val_bits), val_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
CellBuilder& CellBuilder::store_long_top(unsigned long long val, unsigned top_bits) {
|
CellBuilder& CellBuilder::store_long_top(unsigned long long val, unsigned top_bits) {
|
||||||
|
|
|
@ -1055,7 +1055,8 @@ std::ostream& operator<<(std::ostream& os, Ref<CellSlice> cs_ref) {
|
||||||
|
|
||||||
// If can_be_special is not null, then it is allowed to load special cell
|
// If can_be_special is not null, then it is allowed to load special cell
|
||||||
// Flag whether loaded cell is actually special will be stored into can_be_special
|
// Flag whether loaded cell is actually special will be stored into can_be_special
|
||||||
VirtualCell::LoadedCell load_cell_slice_impl(const Ref<Cell>& cell, bool* can_be_special) {
|
VirtualCell::LoadedCell load_cell_slice_impl(Ref<Cell> cell, bool* can_be_special) {
|
||||||
|
while (true) {
|
||||||
auto* vm_state_interface = VmStateInterface::get();
|
auto* vm_state_interface = VmStateInterface::get();
|
||||||
if (vm_state_interface) {
|
if (vm_state_interface) {
|
||||||
vm_state_interface->register_cell_load(cell->get_hash());
|
vm_state_interface->register_cell_load(cell->get_hash());
|
||||||
|
@ -1080,8 +1081,9 @@ VirtualCell::LoadedCell load_cell_slice_impl(const Ref<Cell>& cell, bool* can_be
|
||||||
DCHECK(cs.size() == Cell::hash_bits + 8);
|
DCHECK(cs.size() == Cell::hash_bits + 8);
|
||||||
auto library_cell = vm_state_interface->load_library(cs.data_bits() + 8);
|
auto library_cell = vm_state_interface->load_library(cs.data_bits() + 8);
|
||||||
if (library_cell.not_null()) {
|
if (library_cell.not_null()) {
|
||||||
//TODO: fix infinity loop
|
cell = library_cell;
|
||||||
return load_cell_slice_impl(library_cell, nullptr);
|
can_be_special = nullptr;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
throw VmError{Excno::cell_und, "failed to load library cell"};
|
throw VmError{Excno::cell_und, "failed to load library cell"};
|
||||||
}
|
}
|
||||||
|
@ -1093,6 +1095,7 @@ VirtualCell::LoadedCell load_cell_slice_impl(const Ref<Cell>& cell, bool* can_be
|
||||||
throw VmError{Excno::cell_und, "unexpected special cell"};
|
throw VmError{Excno::cell_und, "unexpected special cell"};
|
||||||
}
|
}
|
||||||
return loaded_cell;
|
return loaded_cell;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CellSlice load_cell_slice(const Ref<Cell>& cell) {
|
CellSlice load_cell_slice(const Ref<Cell>& cell) {
|
||||||
|
|
|
@ -105,6 +105,43 @@ int exec_dump_value(VmState* st, unsigned arg) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int exec_dump_string(VmState* st) {
|
||||||
|
VM_LOG(st) << "execute STRDUMP";
|
||||||
|
if (!vm_debug_enabled) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stack& stack = st->get_stack();
|
||||||
|
|
||||||
|
if (stack.depth() > 0){
|
||||||
|
auto cs = stack[0].as_slice();
|
||||||
|
|
||||||
|
if (cs.not_null()) { // wanted t_slice
|
||||||
|
auto size = cs->size();
|
||||||
|
|
||||||
|
if (size % 8 == 0) {
|
||||||
|
auto cnt = size / 8;
|
||||||
|
|
||||||
|
unsigned char tmp[128];
|
||||||
|
cs.write().fetch_bytes(tmp, cnt);
|
||||||
|
std::string s{tmp, tmp + cnt};
|
||||||
|
|
||||||
|
std::cerr << "#DEBUG#: " << s << std::endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cerr << "#DEBUG#: slice contains not valid bits count" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
std::cerr << "#DEBUG#: is not a slice" << std::endl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cerr << "#DEBUG#: s0 is absent" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void register_debug_ops(OpcodeTable& cp0) {
|
void register_debug_ops(OpcodeTable& cp0) {
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
if (!vm_debug_enabled) {
|
if (!vm_debug_enabled) {
|
||||||
|
@ -113,7 +150,9 @@ void register_debug_ops(OpcodeTable& cp0) {
|
||||||
} else {
|
} else {
|
||||||
// NB: all non-redefined opcodes in fe00..feff should be redirected to dummy debug definitions
|
// NB: all non-redefined opcodes in fe00..feff should be redirected to dummy debug definitions
|
||||||
cp0.insert(OpcodeInstr::mksimple(0xfe00, 16, "DUMPSTK", exec_dump_stack))
|
cp0.insert(OpcodeInstr::mksimple(0xfe00, 16, "DUMPSTK", exec_dump_stack))
|
||||||
.insert(OpcodeInstr::mkfixedrange(0xfe01, 0xfe20, 16, 8, instr::dump_1c_and(0xff, "DEBUG "), exec_dummy_debug))
|
.insert(OpcodeInstr::mkfixedrange(0xfe01, 0xfe14, 16, 8, instr::dump_1c_and(0xff, "DEBUG "), exec_dummy_debug))
|
||||||
|
.insert(OpcodeInstr::mksimple(0xfe14, 16,"STRDUMP", exec_dump_string))
|
||||||
|
.insert(OpcodeInstr::mkfixedrange(0xfe15, 0xfe20, 16, 8, instr::dump_1c_and(0xff, "DEBUG "), exec_dummy_debug))
|
||||||
.insert(OpcodeInstr::mkfixed(0xfe2, 12, 4, instr::dump_1sr("DUMP"), exec_dump_value))
|
.insert(OpcodeInstr::mkfixed(0xfe2, 12, 4, instr::dump_1sr("DUMP"), exec_dump_value))
|
||||||
.insert(OpcodeInstr::mkfixedrange(0xfe30, 0xfef0, 16, 8, instr::dump_1c_and(0xff, "DEBUG "), exec_dummy_debug))
|
.insert(OpcodeInstr::mkfixedrange(0xfe30, 0xfef0, 16, 8, instr::dump_1c_and(0xff, "DEBUG "), exec_dummy_debug))
|
||||||
.insert(OpcodeInstr::mkext(0xfef, 12, 4, dump_dummy_debug_str, exec_dummy_debug_str, compute_len_debug_str));
|
.insert(OpcodeInstr::mkext(0xfef, 12, 4, dump_dummy_debug_str, exec_dummy_debug_str, compute_len_debug_str));
|
||||||
|
|
|
@ -313,7 +313,7 @@ td::Status LargeBocSerializer::serialize(td::FileFd& fd, int mode) {
|
||||||
return td::Status::Error("bag of cells is too large");
|
return td::Status::Error("bag of cells is too large");
|
||||||
}
|
}
|
||||||
|
|
||||||
boc_writers::FileWriter writer{fd, info.total_size};
|
boc_writers::FileWriter writer{fd, (size_t) info.total_size};
|
||||||
auto store_ref = [&](unsigned long long value) {
|
auto store_ref = [&](unsigned long long value) {
|
||||||
writer.store_uint(value, info.ref_byte_size);
|
writer.store_uint(value, info.ref_byte_size);
|
||||||
};
|
};
|
||||||
|
|
|
@ -90,15 +90,27 @@ void StackEntry::dump(std::ostream& os) const {
|
||||||
os << dec_string(as_int());
|
os << dec_string(as_int());
|
||||||
break;
|
break;
|
||||||
case t_cell:
|
case t_cell:
|
||||||
|
if (ref.not_null()) {
|
||||||
os << "C{" << static_cast<Ref<Cell>>(ref)->get_hash().to_hex() << "}";
|
os << "C{" << static_cast<Ref<Cell>>(ref)->get_hash().to_hex() << "}";
|
||||||
|
} else {
|
||||||
|
os << "C{null}";
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case t_builder:
|
case t_builder:
|
||||||
|
if (ref.not_null()) {
|
||||||
os << "BC{" << static_cast<Ref<CellBuilder>>(ref)->to_hex() << "}";
|
os << "BC{" << static_cast<Ref<CellBuilder>>(ref)->to_hex() << "}";
|
||||||
|
} else {
|
||||||
|
os << "BC{null}";
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case t_slice: {
|
case t_slice: {
|
||||||
|
if (ref.not_null()) {
|
||||||
os << "CS{";
|
os << "CS{";
|
||||||
static_cast<Ref<CellSlice>>(ref)->dump(os, 1, false);
|
static_cast<Ref<CellSlice>>(ref)->dump(os, 1, false);
|
||||||
os << '}';
|
os << '}';
|
||||||
|
} else {
|
||||||
|
os << "CS{null}";
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case t_string:
|
case t_string:
|
||||||
|
|
|
@ -474,7 +474,7 @@ int exec_store_var_integer(VmState* st, int len_bits, bool sgnd, bool quiet) {
|
||||||
stack.check_underflow(2);
|
stack.check_underflow(2);
|
||||||
auto x = stack.pop_int();
|
auto x = stack.pop_int();
|
||||||
auto cbr = stack.pop_builder();
|
auto cbr = stack.pop_builder();
|
||||||
unsigned len = ((x->bit_size(sgnd) + 7) >> 3);
|
unsigned len = (((unsigned)x->bit_size(sgnd) + 7) >> 3);
|
||||||
if (len >= (1u << len_bits)) {
|
if (len >= (1u << len_bits)) {
|
||||||
throw VmError{Excno::range_chk};
|
throw VmError{Excno::range_chk};
|
||||||
}
|
}
|
||||||
|
|
|
@ -503,7 +503,7 @@ int VmState::run() {
|
||||||
|
|
||||||
bool VmState::try_commit() {
|
bool VmState::try_commit() {
|
||||||
if (cr.d[0].not_null() && cr.d[1].not_null() && cr.d[0]->get_depth() <= max_data_depth &&
|
if (cr.d[0].not_null() && cr.d[1].not_null() && cr.d[0]->get_depth() <= max_data_depth &&
|
||||||
cr.d[1]->get_depth() <= max_data_depth) {
|
cr.d[1]->get_depth() <= max_data_depth && cr.d[0]->get_level() == 0 && cr.d[1]->get_level() == 0) {
|
||||||
cstate.c4 = cr.d[0];
|
cstate.c4 = cr.d[0];
|
||||||
cstate.c5 = cr.d[1];
|
cstate.c5 = cr.d[1];
|
||||||
cstate.committed = true;
|
cstate.committed = true;
|
||||||
|
|
36
fec/fec.cpp
36
fec/fec.cpp
|
@ -19,6 +19,7 @@
|
||||||
#include "fec.h"
|
#include "fec.h"
|
||||||
#include "td/utils/overloaded.h"
|
#include "td/utils/overloaded.h"
|
||||||
#include "auto/tl/ton_api.hpp"
|
#include "auto/tl/ton_api.hpp"
|
||||||
|
#include "td/utils/misc.h"
|
||||||
|
|
||||||
namespace ton {
|
namespace ton {
|
||||||
|
|
||||||
|
@ -98,23 +99,36 @@ td::uint32 FecType::symbol_size() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
td::Result<FecType> FecType::create(tl_object_ptr<ton_api::fec_Type> obj) {
|
td::Result<FecType> FecType::create(tl_object_ptr<ton_api::fec_Type> obj) {
|
||||||
|
td::int32 data_size_int, symbol_size_int, symbols_count_int;
|
||||||
|
ton_api::downcast_call(*obj, td::overloaded([&](const auto &obj) {
|
||||||
|
data_size_int = obj.data_size_;
|
||||||
|
symbol_size_int = obj.symbol_size_;
|
||||||
|
symbols_count_int = obj.symbols_count_;
|
||||||
|
}));
|
||||||
|
TRY_RESULT(data_size, td::narrow_cast_safe<size_t>(data_size_int));
|
||||||
|
TRY_RESULT(symbol_size, td::narrow_cast_safe<size_t>(symbol_size_int));
|
||||||
|
TRY_RESULT(symbols_count, td::narrow_cast_safe<size_t>(symbols_count_int));
|
||||||
|
|
||||||
|
if (symbol_size == 0) {
|
||||||
|
return td::Status::Error("invalid fec type: symbol_size is 0");
|
||||||
|
}
|
||||||
|
if (symbol_size > 1 << 11) {
|
||||||
|
return td::Status::Error("invalid fec type: symbol_size is too big");
|
||||||
|
}
|
||||||
|
if (symbols_count != (data_size + symbol_size - 1) / symbol_size) {
|
||||||
|
return td::Status::Error("invalid fec type: wrong symbols_count");
|
||||||
|
}
|
||||||
FecType T;
|
FecType T;
|
||||||
ton_api::downcast_call(
|
ton_api::downcast_call(*obj,
|
||||||
*obj.get(), td::overloaded(
|
td::overloaded(
|
||||||
[&](const ton_api::fec_raptorQ &obj) {
|
[&](const ton_api::fec_raptorQ &obj) {
|
||||||
T.type_ = td::fec::RaptorQEncoder::Parameters{static_cast<size_t>(obj.data_size_),
|
T.type_ = td::fec::RaptorQEncoder::Parameters{data_size, symbol_size, symbols_count};
|
||||||
static_cast<size_t>(obj.symbol_size_),
|
|
||||||
static_cast<size_t>(obj.symbols_count_)};
|
|
||||||
},
|
},
|
||||||
[&](const ton_api::fec_roundRobin &obj) {
|
[&](const ton_api::fec_roundRobin &obj) {
|
||||||
T.type_ = td::fec::RoundRobinEncoder::Parameters{static_cast<size_t>(obj.data_size_),
|
T.type_ = td::fec::RoundRobinEncoder::Parameters{data_size, symbol_size, symbols_count};
|
||||||
static_cast<size_t>(obj.symbol_size_),
|
|
||||||
static_cast<size_t>(obj.symbols_count_)};
|
|
||||||
},
|
},
|
||||||
[&](const ton_api::fec_online &obj) {
|
[&](const ton_api::fec_online &obj) {
|
||||||
T.type_ = td::fec::OnlineEncoder::Parameters{static_cast<size_t>(obj.data_size_),
|
T.type_ = td::fec::OnlineEncoder::Parameters{data_size, symbol_size, symbols_count};
|
||||||
static_cast<size_t>(obj.symbol_size_),
|
|
||||||
static_cast<size_t>(obj.symbols_count_)};
|
|
||||||
}));
|
}));
|
||||||
return T;
|
return T;
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,9 +58,7 @@ void HttpConnection::loop() {
|
||||||
}
|
}
|
||||||
TRY_STATUS(buffered_fd_.flush_write());
|
TRY_STATUS(buffered_fd_.flush_write());
|
||||||
if (writing_payload_ && buffered_fd_.left_unwritten() < fd_high_watermark()) {
|
if (writing_payload_ && buffered_fd_.left_unwritten() < fd_high_watermark()) {
|
||||||
auto w = buffered_fd_.left_unwritten();
|
written = continue_payload_write();
|
||||||
continue_payload_write();
|
|
||||||
written = buffered_fd_.left_unwritten() > w;
|
|
||||||
}
|
}
|
||||||
if (close_after_write_ && !writing_payload_ && !buffered_fd_.left_unwritten()) {
|
if (close_after_write_ && !writing_payload_ && !buffered_fd_.left_unwritten()) {
|
||||||
LOG(INFO) << "close after write";
|
LOG(INFO) << "close after write";
|
||||||
|
@ -120,7 +118,7 @@ void HttpConnection::write_payload(std::shared_ptr<HttpPayload> payload) {
|
||||||
|
|
||||||
class Cb : public HttpPayload::Callback {
|
class Cb : public HttpPayload::Callback {
|
||||||
public:
|
public:
|
||||||
Cb(td::actor::ActorId<HttpConnection> conn) : conn_(conn) {
|
Cb(td::actor::ActorId<HttpConnection> conn, size_t watermark) : conn_(conn), watermark_(watermark) {
|
||||||
}
|
}
|
||||||
void run(size_t ready_bytes) override {
|
void run(size_t ready_bytes) override {
|
||||||
if (!reached_ && ready_bytes >= watermark_) {
|
if (!reached_ && ready_bytes >= watermark_) {
|
||||||
|
@ -135,23 +133,23 @@ void HttpConnection::write_payload(std::shared_ptr<HttpPayload> payload) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t watermark_ = chunk_size();
|
|
||||||
bool reached_ = false;
|
|
||||||
|
|
||||||
td::actor::ActorId<HttpConnection> conn_;
|
td::actor::ActorId<HttpConnection> conn_;
|
||||||
|
size_t watermark_;
|
||||||
|
bool reached_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
writing_payload_->add_callback(std::make_unique<Cb>(actor_id(this)));
|
writing_payload_->add_callback(std::make_unique<Cb>(
|
||||||
|
actor_id(this), writing_payload_->payload_type() == HttpPayload::PayloadType::pt_tunnel ? 1 : chunk_size()));
|
||||||
continue_payload_write();
|
continue_payload_write();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpConnection::continue_payload_write() {
|
bool HttpConnection::continue_payload_write() {
|
||||||
if (!writing_payload_) {
|
if (!writing_payload_) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (writing_payload_->is_error()) {
|
if (writing_payload_->is_error()) {
|
||||||
stop();
|
stop();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto t = writing_payload_->payload_type();
|
auto t = writing_payload_->payload_type();
|
||||||
|
@ -159,19 +157,24 @@ void HttpConnection::continue_payload_write() {
|
||||||
t = HttpPayload::PayloadType::pt_chunked;
|
t = HttpPayload::PayloadType::pt_chunked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool wrote = false;
|
||||||
while (!writing_payload_->written()) {
|
while (!writing_payload_->written()) {
|
||||||
if (buffered_fd_.left_unwritten() > fd_high_watermark()) {
|
if (buffered_fd_.left_unwritten() > fd_high_watermark()) {
|
||||||
return;
|
return wrote;
|
||||||
}
|
}
|
||||||
if (!writing_payload_->parse_completed() && writing_payload_->ready_bytes() < chunk_size()) {
|
bool is_tunnel = writing_payload_->payload_type() == HttpPayload::PayloadType::pt_tunnel;
|
||||||
return;
|
if (!is_tunnel && !writing_payload_->parse_completed() && writing_payload_->ready_bytes() < chunk_size()) {
|
||||||
|
return wrote;
|
||||||
}
|
}
|
||||||
writing_payload_->store_http(buffered_fd_.output_buffer(), chunk_size(), t);
|
if (is_tunnel && writing_payload_->ready_bytes() == 0) {
|
||||||
|
return wrote;
|
||||||
|
}
|
||||||
|
wrote |= writing_payload_->store_http(buffered_fd_.output_buffer(), chunk_size(), t);
|
||||||
}
|
}
|
||||||
if (writing_payload_->parse_completed() && writing_payload_->written()) {
|
if (writing_payload_->parse_completed() && writing_payload_->written()) {
|
||||||
payload_written();
|
payload_written();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
return wrote;
|
||||||
}
|
}
|
||||||
|
|
||||||
td::Status HttpConnection::read_payload(HttpResponse *response) {
|
td::Status HttpConnection::read_payload(HttpResponse *response) {
|
||||||
|
|
|
@ -65,9 +65,7 @@ class HttpConnection : public td::actor::Actor, public td::ObserverBase {
|
||||||
void send_request(std::unique_ptr<HttpRequest> request, std::shared_ptr<HttpPayload> payload);
|
void send_request(std::unique_ptr<HttpRequest> request, std::shared_ptr<HttpPayload> payload);
|
||||||
void send_response(std::unique_ptr<HttpResponse> response, std::shared_ptr<HttpPayload> payload);
|
void send_response(std::unique_ptr<HttpResponse> response, std::shared_ptr<HttpPayload> payload);
|
||||||
void write_payload(std::shared_ptr<HttpPayload> payload);
|
void write_payload(std::shared_ptr<HttpPayload> payload);
|
||||||
void continue_payload_write();
|
bool continue_payload_write();
|
||||||
td::Status receive_request();
|
|
||||||
td::Status receive_response();
|
|
||||||
td::Status read_payload(HttpRequest *request);
|
td::Status read_payload(HttpRequest *request);
|
||||||
td::Status read_payload(HttpResponse *response);
|
td::Status read_payload(HttpResponse *response);
|
||||||
td::Status read_payload(std::shared_ptr<HttpPayload> payload);
|
td::Status read_payload(std::shared_ptr<HttpPayload> payload);
|
||||||
|
|
|
@ -44,13 +44,22 @@ void HttpInboundConnection::send_server_error() {
|
||||||
loop();
|
loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpInboundConnection::send_proxy_error() {
|
void HttpInboundConnection::send_proxy_error(td::Status error) {
|
||||||
|
if (error.code() == ErrorCode::timeout) {
|
||||||
|
static const auto s =
|
||||||
|
"HTTP/1.1 504 Gateway Timeout\r\n"
|
||||||
|
"Connection: keep-alive\r\n"
|
||||||
|
"Content-length: 0\r\n"
|
||||||
|
"\r\n";
|
||||||
|
buffered_fd_.output_buffer().append(td::Slice(s, strlen(s)));
|
||||||
|
} else {
|
||||||
static const auto s =
|
static const auto s =
|
||||||
"HTTP/1.1 502 Bad Gateway\r\n"
|
"HTTP/1.1 502 Bad Gateway\r\n"
|
||||||
"Connection: keep-alive\r\n"
|
"Connection: keep-alive\r\n"
|
||||||
"Content-length: 0\r\n"
|
"Content-length: 0\r\n"
|
||||||
"\r\n";
|
"\r\n";
|
||||||
buffered_fd_.output_buffer().append(td::Slice(s, strlen(s)));
|
buffered_fd_.output_buffer().append(td::Slice(s, strlen(s)));
|
||||||
|
}
|
||||||
loop();
|
loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +92,7 @@ td::Status HttpInboundConnection::receive(td::ChainBufferReader &input) {
|
||||||
auto a = R.move_as_ok();
|
auto a = R.move_as_ok();
|
||||||
td::actor::send_closure(SelfId, &HttpInboundConnection::send_answer, std::move(a.first), std::move(a.second));
|
td::actor::send_closure(SelfId, &HttpInboundConnection::send_answer, std::move(a.first), std::move(a.second));
|
||||||
} else {
|
} else {
|
||||||
td::actor::send_closure(SelfId, &HttpInboundConnection::send_proxy_error);
|
td::actor::send_closure(SelfId, &HttpInboundConnection::send_proxy_error, R.move_as_error());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
http_callback_->receive_request(std::move(cur_request_), payload, std::move(P));
|
http_callback_->receive_request(std::move(cur_request_), payload, std::move(P));
|
||||||
|
|
|
@ -35,7 +35,8 @@ class HttpInboundConnection : public HttpConnection {
|
||||||
td::Status receive_eof() override {
|
td::Status receive_eof() override {
|
||||||
found_eof_ = true;
|
found_eof_ = true;
|
||||||
if (reading_payload_) {
|
if (reading_payload_) {
|
||||||
if (reading_payload_->payload_type() != HttpPayload::PayloadType::pt_eof) {
|
if (reading_payload_->payload_type() != HttpPayload::PayloadType::pt_eof &&
|
||||||
|
reading_payload_->payload_type() != HttpPayload::PayloadType::pt_tunnel) {
|
||||||
return td::Status::Error("unexpected EOF");
|
return td::Status::Error("unexpected EOF");
|
||||||
} else {
|
} else {
|
||||||
reading_payload_->complete_parse();
|
reading_payload_->complete_parse();
|
||||||
|
@ -53,7 +54,7 @@ class HttpInboundConnection : public HttpConnection {
|
||||||
|
|
||||||
void send_client_error();
|
void send_client_error();
|
||||||
void send_server_error();
|
void send_server_error();
|
||||||
void send_proxy_error();
|
void send_proxy_error(td::Status error);
|
||||||
|
|
||||||
void payload_written() override {
|
void payload_written() override {
|
||||||
writing_payload_ = nullptr;
|
writing_payload_ = nullptr;
|
||||||
|
|
|
@ -44,7 +44,8 @@ class HttpOutboundConnection : public HttpConnection {
|
||||||
td::Status receive_eof() override {
|
td::Status receive_eof() override {
|
||||||
found_eof_ = true;
|
found_eof_ = true;
|
||||||
if (reading_payload_) {
|
if (reading_payload_) {
|
||||||
if (reading_payload_->payload_type() != HttpPayload::PayloadType::pt_eof) {
|
if (reading_payload_->payload_type() != HttpPayload::PayloadType::pt_eof &&
|
||||||
|
reading_payload_->payload_type() != HttpPayload::PayloadType::pt_tunnel) {
|
||||||
return td::Status::Error("unexpected EOF");
|
return td::Status::Error("unexpected EOF");
|
||||||
} else {
|
} else {
|
||||||
LOG(INFO) << "stopping (EOF payload)";
|
LOG(INFO) << "stopping (EOF payload)";
|
||||||
|
|
|
@ -164,6 +164,8 @@ td::Result<std::shared_ptr<HttpPayload>> HttpRequest::create_empty_payload() {
|
||||||
|
|
||||||
if (!need_payload()) {
|
if (!need_payload()) {
|
||||||
return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_empty);
|
return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_empty);
|
||||||
|
} else if (method_ == "CONNECT") {
|
||||||
|
return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_tunnel, low_watermark(), high_watermark());
|
||||||
} else if (found_content_length_) {
|
} else if (found_content_length_) {
|
||||||
return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_content_length, low_watermark(), high_watermark(),
|
return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_content_length, low_watermark(), high_watermark(),
|
||||||
content_length_);
|
content_length_);
|
||||||
|
@ -175,7 +177,7 @@ td::Result<std::shared_ptr<HttpPayload>> HttpRequest::create_empty_payload() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HttpRequest::need_payload() const {
|
bool HttpRequest::need_payload() const {
|
||||||
return found_content_length_ || found_transfer_encoding_;
|
return found_content_length_ || found_transfer_encoding_ || method_ == "CONNECT";
|
||||||
}
|
}
|
||||||
|
|
||||||
td::Status HttpRequest::add_header(HttpHeader header) {
|
td::Status HttpRequest::add_header(HttpHeader header) {
|
||||||
|
@ -191,9 +193,6 @@ td::Status HttpRequest::add_header(HttpHeader header) {
|
||||||
if (found_transfer_encoding_ || found_content_length_) {
|
if (found_transfer_encoding_ || found_content_length_) {
|
||||||
return td::Status::Error("duplicate Content-Length/Transfer-Encoding");
|
return td::Status::Error("duplicate Content-Length/Transfer-Encoding");
|
||||||
}
|
}
|
||||||
if (len > HttpRequest::max_payload_size()) {
|
|
||||||
return td::Status::Error("too big Content-Length");
|
|
||||||
}
|
|
||||||
content_length_ = len;
|
content_length_ = len;
|
||||||
found_content_length_ = true;
|
found_content_length_ = true;
|
||||||
} else if (lc_name == "transfer-encoding") {
|
} else if (lc_name == "transfer-encoding") {
|
||||||
|
@ -284,7 +283,8 @@ td::Status HttpPayload::parse(td::ChainBufferReader &input) {
|
||||||
case PayloadType::pt_empty:
|
case PayloadType::pt_empty:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
case PayloadType::pt_eof:
|
case PayloadType::pt_eof:
|
||||||
cur_chunk_size_ = 1 << 30;
|
case PayloadType::pt_tunnel:
|
||||||
|
cur_chunk_size_ = 1LL << 60;
|
||||||
break;
|
break;
|
||||||
case PayloadType::pt_chunked:
|
case PayloadType::pt_chunked:
|
||||||
state_ = ParseState::reading_crlf;
|
state_ = ParseState::reading_crlf;
|
||||||
|
@ -480,17 +480,18 @@ void HttpPayload::run_callbacks() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpPayload::store_http(td::ChainBufferWriter &output, size_t max_size, HttpPayload::PayloadType store_type) {
|
bool HttpPayload::store_http(td::ChainBufferWriter &output, size_t max_size, HttpPayload::PayloadType store_type) {
|
||||||
if (store_type == PayloadType::pt_empty) {
|
if (store_type == PayloadType::pt_empty) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
slice_gc();
|
slice_gc();
|
||||||
|
bool wrote = false;
|
||||||
while (chunks_.size() > 0 && max_size > 0) {
|
while (chunks_.size() > 0 && max_size > 0) {
|
||||||
auto cur_state = state_.load(std::memory_order_consume);
|
auto cur_state = state_.load(std::memory_order_consume);
|
||||||
auto s = get_slice(max_size);
|
auto s = get_slice(max_size);
|
||||||
if (s.size() == 0) {
|
if (s.size() == 0) {
|
||||||
if (cur_state != ParseState::reading_trailer && cur_state != ParseState::completed) {
|
if (cur_state != ParseState::reading_trailer && cur_state != ParseState::completed) {
|
||||||
return;
|
return wrote;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -500,28 +501,33 @@ void HttpPayload::store_http(td::ChainBufferWriter &output, size_t max_size, Htt
|
||||||
if (store_type == PayloadType::pt_chunked) {
|
if (store_type == PayloadType::pt_chunked) {
|
||||||
char buf[64];
|
char buf[64];
|
||||||
::sprintf(buf, "%lx\r\n", s.size());
|
::sprintf(buf, "%lx\r\n", s.size());
|
||||||
output.append(td::Slice(buf, strlen(buf)));
|
auto slice = td::Slice(buf, strlen(buf));
|
||||||
|
wrote |= !slice.empty();
|
||||||
|
output.append(slice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wrote |= !s.empty();
|
||||||
output.append(std::move(s));
|
output.append(std::move(s));
|
||||||
|
|
||||||
if (store_type == PayloadType::pt_chunked) {
|
if (store_type == PayloadType::pt_chunked) {
|
||||||
output.append(td::Slice("\r\n", 2));
|
output.append(td::Slice("\r\n", 2));
|
||||||
|
wrote = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chunks_.size() != 0) {
|
if (chunks_.size() != 0 || !parse_completed()) {
|
||||||
return;
|
return wrote;
|
||||||
}
|
}
|
||||||
if (!written_zero_chunk_) {
|
if (!written_zero_chunk_) {
|
||||||
if (store_type == PayloadType::pt_chunked) {
|
if (store_type == PayloadType::pt_chunked) {
|
||||||
output.append(td::Slice("0\r\n", 3));
|
output.append(td::Slice("0\r\n", 3));
|
||||||
|
wrote = true;
|
||||||
}
|
}
|
||||||
written_zero_chunk_ = true;
|
written_zero_chunk_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store_type != PayloadType::pt_chunked) {
|
if (store_type != PayloadType::pt_chunked) {
|
||||||
written_trailer_ = true;
|
written_trailer_ = true;
|
||||||
return;
|
return wrote;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (max_size > 0) {
|
while (max_size > 0) {
|
||||||
|
@ -529,15 +535,16 @@ void HttpPayload::store_http(td::ChainBufferWriter &output, size_t max_size, Htt
|
||||||
HttpHeader h = get_header();
|
HttpHeader h = get_header();
|
||||||
if (h.empty()) {
|
if (h.empty()) {
|
||||||
if (cur_state != ParseState::completed) {
|
if (cur_state != ParseState::completed) {
|
||||||
return;
|
return wrote;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto s = h.size();
|
auto s = h.size();
|
||||||
h.store_http(output);
|
h.store_http(output);
|
||||||
|
wrote = true;
|
||||||
if (max_size <= s) {
|
if (max_size <= s) {
|
||||||
return;
|
return wrote;
|
||||||
}
|
}
|
||||||
max_size -= s;
|
max_size -= s;
|
||||||
}
|
}
|
||||||
|
@ -545,7 +552,9 @@ void HttpPayload::store_http(td::ChainBufferWriter &output, size_t max_size, Htt
|
||||||
if (!written_trailer_) {
|
if (!written_trailer_) {
|
||||||
output.append(td::Slice("\r\n", 2));
|
output.append(td::Slice("\r\n", 2));
|
||||||
written_trailer_ = true;
|
written_trailer_ = true;
|
||||||
|
wrote = true;
|
||||||
}
|
}
|
||||||
|
return wrote;
|
||||||
}
|
}
|
||||||
|
|
||||||
tl_object_ptr<ton_api::http_payloadPart> HttpPayload::store_tl(size_t max_size) {
|
tl_object_ptr<ton_api::http_payloadPart> HttpPayload::store_tl(size_t max_size) {
|
||||||
|
@ -729,17 +738,18 @@ td::Result<std::unique_ptr<HttpResponse>> HttpResponse::parse(std::unique_ptr<Ht
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse::HttpResponse(std::string proto_version, td::uint32 code, std::string reason, bool force_no_payload,
|
HttpResponse::HttpResponse(std::string proto_version, td::uint32 code, std::string reason, bool force_no_payload,
|
||||||
bool keep_alive)
|
bool keep_alive, bool is_tunnel)
|
||||||
: proto_version_(std::move(proto_version))
|
: proto_version_(std::move(proto_version))
|
||||||
, code_(code)
|
, code_(code)
|
||||||
, reason_(std::move(reason))
|
, reason_(std::move(reason))
|
||||||
, force_no_payload_(force_no_payload)
|
, force_no_payload_(force_no_payload)
|
||||||
, force_no_keep_alive_(!keep_alive) {
|
, force_no_keep_alive_(!keep_alive)
|
||||||
|
, is_tunnel_(is_tunnel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
td::Result<std::unique_ptr<HttpResponse>> HttpResponse::create(std::string proto_version, td::uint32 code,
|
td::Result<std::unique_ptr<HttpResponse>> HttpResponse::create(std::string proto_version, td::uint32 code,
|
||||||
std::string reason, bool force_no_payload,
|
std::string reason, bool force_no_payload,
|
||||||
bool keep_alive) {
|
bool keep_alive, bool is_tunnel) {
|
||||||
if (proto_version != "HTTP/1.0" && proto_version != "HTTP/1.1") {
|
if (proto_version != "HTTP/1.0" && proto_version != "HTTP/1.1") {
|
||||||
return td::Status::Error(PSTRING() << "unsupported http version '" << proto_version << "'");
|
return td::Status::Error(PSTRING() << "unsupported http version '" << proto_version << "'");
|
||||||
}
|
}
|
||||||
|
@ -749,7 +759,7 @@ td::Result<std::unique_ptr<HttpResponse>> HttpResponse::create(std::string proto
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_unique<HttpResponse>(std::move(proto_version), code, std::move(reason), force_no_payload,
|
return std::make_unique<HttpResponse>(std::move(proto_version), code, std::move(reason), force_no_payload,
|
||||||
keep_alive);
|
keep_alive, is_tunnel);
|
||||||
}
|
}
|
||||||
|
|
||||||
td::Status HttpResponse::complete_parse_header() {
|
td::Status HttpResponse::complete_parse_header() {
|
||||||
|
@ -767,6 +777,8 @@ td::Result<std::shared_ptr<HttpPayload>> HttpResponse::create_empty_payload() {
|
||||||
|
|
||||||
if (!need_payload()) {
|
if (!need_payload()) {
|
||||||
return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_empty);
|
return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_empty);
|
||||||
|
} else if (is_tunnel_) {
|
||||||
|
return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_tunnel, low_watermark(), high_watermark());
|
||||||
} else if (found_content_length_) {
|
} else if (found_content_length_) {
|
||||||
return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_content_length, low_watermark(), high_watermark(),
|
return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_content_length, low_watermark(), high_watermark(),
|
||||||
content_length_);
|
content_length_);
|
||||||
|
@ -794,9 +806,6 @@ td::Status HttpResponse::add_header(HttpHeader header) {
|
||||||
if (found_transfer_encoding_ || found_content_length_) {
|
if (found_transfer_encoding_ || found_content_length_) {
|
||||||
return td::Status::Error("duplicate Content-Length/Transfer-Encoding");
|
return td::Status::Error("duplicate Content-Length/Transfer-Encoding");
|
||||||
}
|
}
|
||||||
if (len > HttpRequest::max_payload_size()) {
|
|
||||||
return td::Status::Error("too big Content-Length");
|
|
||||||
}
|
|
||||||
content_length_ = len;
|
content_length_ = len;
|
||||||
found_content_length_ = true;
|
found_content_length_ = true;
|
||||||
} else if (lc_name == "transfer-encoding") {
|
} else if (lc_name == "transfer-encoding") {
|
||||||
|
@ -828,11 +837,13 @@ void HttpResponse::store_http(td::ChainBufferWriter &output) {
|
||||||
for (auto &x : options_) {
|
for (auto &x : options_) {
|
||||||
x.store_http(output);
|
x.store_http(output);
|
||||||
}
|
}
|
||||||
|
if (!is_tunnel_) {
|
||||||
if (keep_alive_) {
|
if (keep_alive_) {
|
||||||
HttpHeader{"Connection", "Keep-Alive"}.store_http(output);
|
HttpHeader{"Connection", "Keep-Alive"}.store_http(output);
|
||||||
} else {
|
} else {
|
||||||
HttpHeader{"Connection", "Close"}.store_http(output);
|
HttpHeader{"Connection", "Close"}.store_http(output);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
output.append(td::Slice("\r\n", 2));
|
output.append(td::Slice("\r\n", 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -847,7 +858,7 @@ tl_object_ptr<ton_api::http_response> HttpResponse::store_tl() {
|
||||||
} else {
|
} else {
|
||||||
headers.push_back(HttpHeader{"Connection", "Close"}.store_tl());
|
headers.push_back(HttpHeader{"Connection", "Close"}.store_tl());
|
||||||
}
|
}
|
||||||
return create_tl_object<ton_api::http_response>(proto_version_, code_, reason_, std::move(headers));
|
return create_tl_object<ton_api::http_response>(proto_version_, code_, reason_, std::move(headers), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
td::Status HttpHeader::basic_check() {
|
td::Status HttpHeader::basic_check() {
|
||||||
|
@ -893,7 +904,9 @@ void answer_error(HttpStatusCode code, std::string reason,
|
||||||
}
|
}
|
||||||
auto response = HttpResponse::create("HTTP/1.0", code, reason, false, false).move_as_ok();
|
auto response = HttpResponse::create("HTTP/1.0", code, reason, false, false).move_as_ok();
|
||||||
response->add_header(HttpHeader{"Content-Length", "0"});
|
response->add_header(HttpHeader{"Content-Length", "0"});
|
||||||
|
response->complete_parse_header();
|
||||||
auto payload = response->create_empty_payload().move_as_ok();
|
auto payload = response->create_empty_payload().move_as_ok();
|
||||||
|
payload->complete_parse();
|
||||||
CHECK(payload->parse_completed());
|
CHECK(payload->parse_completed());
|
||||||
promise.set_value(std::make_pair(std::move(response), std::move(payload)));
|
promise.set_value(std::make_pair(std::move(response), std::move(payload)));
|
||||||
}
|
}
|
||||||
|
|
19
http/http.h
19
http/http.h
|
@ -64,7 +64,7 @@ td::Result<HttpHeader> get_header(std::string line);
|
||||||
|
|
||||||
class HttpPayload {
|
class HttpPayload {
|
||||||
public:
|
public:
|
||||||
enum class PayloadType { pt_empty, pt_eof, pt_chunked, pt_content_length };
|
enum class PayloadType { pt_empty, pt_eof, pt_chunked, pt_content_length, pt_tunnel };
|
||||||
HttpPayload(PayloadType t, size_t low_watermark, size_t high_watermark, td::uint64 size)
|
HttpPayload(PayloadType t, size_t low_watermark, size_t high_watermark, td::uint64 size)
|
||||||
: type_(t), low_watermark_(low_watermark), high_watermark_(high_watermark), cur_chunk_size_(size) {
|
: type_(t), low_watermark_(low_watermark), high_watermark_(high_watermark), cur_chunk_size_(size) {
|
||||||
CHECK(t == PayloadType::pt_content_length);
|
CHECK(t == PayloadType::pt_content_length);
|
||||||
|
@ -75,17 +75,15 @@ class HttpPayload {
|
||||||
CHECK(t != PayloadType::pt_content_length);
|
CHECK(t != PayloadType::pt_content_length);
|
||||||
CHECK(t != PayloadType::pt_empty);
|
CHECK(t != PayloadType::pt_empty);
|
||||||
switch (t) {
|
switch (t) {
|
||||||
case PayloadType::pt_empty:
|
|
||||||
UNREACHABLE();
|
|
||||||
case PayloadType::pt_eof:
|
case PayloadType::pt_eof:
|
||||||
|
case PayloadType::pt_tunnel:
|
||||||
state_ = ParseState::reading_chunk_data;
|
state_ = ParseState::reading_chunk_data;
|
||||||
break;
|
break;
|
||||||
case PayloadType::pt_chunked:
|
case PayloadType::pt_chunked:
|
||||||
state_ = ParseState::reading_chunk_header;
|
state_ = ParseState::reading_chunk_header;
|
||||||
break;
|
break;
|
||||||
case PayloadType::pt_content_length:
|
default:
|
||||||
state_ = ParseState::reading_chunk_data;
|
UNREACHABLE();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HttpPayload(PayloadType t) : type_(t) {
|
HttpPayload(PayloadType t) : type_(t) {
|
||||||
|
@ -136,7 +134,7 @@ class HttpPayload {
|
||||||
void slice_gc();
|
void slice_gc();
|
||||||
HttpHeader get_header();
|
HttpHeader get_header();
|
||||||
|
|
||||||
void store_http(td::ChainBufferWriter &output, size_t max_size, HttpPayload::PayloadType store_type);
|
bool store_http(td::ChainBufferWriter &output, size_t max_size, HttpPayload::PayloadType store_type);
|
||||||
tl_object_ptr<ton_api::http_payloadPart> store_tl(size_t max_size);
|
tl_object_ptr<ton_api::http_payloadPart> store_tl(size_t max_size);
|
||||||
|
|
||||||
bool written() const {
|
bool written() const {
|
||||||
|
@ -267,9 +265,11 @@ class HttpResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
static td::Result<std::unique_ptr<HttpResponse>> create(std::string proto_version, td::uint32 code,
|
static td::Result<std::unique_ptr<HttpResponse>> create(std::string proto_version, td::uint32 code,
|
||||||
std::string reason, bool force_no_payload, bool keep_alive);
|
std::string reason, bool force_no_payload, bool keep_alive,
|
||||||
|
bool is_tunnel = false);
|
||||||
|
|
||||||
HttpResponse(std::string proto_version, td::uint32 code, std::string reason, bool force_no_payload, bool keep_alive);
|
HttpResponse(std::string proto_version, td::uint32 code, std::string reason, bool force_no_payload, bool keep_alive,
|
||||||
|
bool is_tunnel = false);
|
||||||
|
|
||||||
bool check_parse_header_completed() const;
|
bool check_parse_header_completed() const;
|
||||||
bool keep_alive() const {
|
bool keep_alive() const {
|
||||||
|
@ -323,6 +323,7 @@ class HttpResponse {
|
||||||
bool keep_alive_ = false;
|
bool keep_alive_ = false;
|
||||||
|
|
||||||
std::vector<HttpHeader> options_;
|
std::vector<HttpHeader> options_;
|
||||||
|
bool is_tunnel_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
void answer_error(HttpStatusCode code, std::string reason,
|
void answer_error(HttpStatusCode code, std::string reason,
|
||||||
|
|
|
@ -1979,10 +1979,11 @@ void TestNode::dns_resolve_finish(ton::WorkchainId workchain, ton::StdSmcAddress
|
||||||
if (!dict.check_for_each([this, &out](Ref<vm::CellSlice> cs, td::ConstBitPtr key, int n) {
|
if (!dict.check_for_each([this, &out](Ref<vm::CellSlice> cs, td::ConstBitPtr key, int n) {
|
||||||
CHECK(n == 256);
|
CHECK(n == 256);
|
||||||
td::Bits256 x{key};
|
td::Bits256 x{key};
|
||||||
/*if (cs.is_null() || cs->size_ext() != 0x10000) {
|
if (cs.is_null() || cs->size_ext() != 0x10000) {
|
||||||
out << "category " << x << " : value is not a reference" << std::endl;
|
out << "category " << x << " : value is not a reference" << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}*/
|
}
|
||||||
|
cs = vm::load_cell_slice_ref(cs->prefetch_ref());
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
(void)show_dns_record(os, x, cs, true);
|
(void)show_dns_record(os, x, cs, true);
|
||||||
out << "category " << x << " : " << os.str() << std::endl;
|
out << "category " << x << " : " << os.str() << std::endl;
|
||||||
|
|
|
@ -28,8 +28,8 @@ td::Result<std::unique_ptr<BroadcastFec>> BroadcastFec::create(Overlay::Broadcas
|
||||||
Overlay::BroadcastDataHash data_hash, td::uint32 flags,
|
Overlay::BroadcastDataHash data_hash, td::uint32 flags,
|
||||||
td::uint32 date, fec::FecType fec_type, bool is_ours) {
|
td::uint32 date, fec::FecType fec_type, bool is_ours) {
|
||||||
auto F = std::make_unique<BroadcastFec>(hash, std::move(src), data_hash, flags, date, std::move(fec_type), is_ours);
|
auto F = std::make_unique<BroadcastFec>(hash, std::move(src), data_hash, flags, date, std::move(fec_type), is_ours);
|
||||||
TRY_STATUS(F->init_fec_type());
|
|
||||||
TRY_STATUS(F->run_checks());
|
TRY_STATUS(F->run_checks());
|
||||||
|
TRY_STATUS(F->init_fec_type());
|
||||||
return std::move(F);
|
return std::move(F);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
|
||||||
|
|
||||||
add_executable(rldp-http-proxy rldp-http-proxy.cpp)
|
add_executable(rldp-http-proxy rldp-http-proxy.cpp DNSResolver.h TonlibClient.h TonlibClient.cpp DNSResolver.cpp)
|
||||||
target_include_directories(rldp-http-proxy PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
|
target_include_directories(rldp-http-proxy PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
|
||||||
target_link_libraries(rldp-http-proxy PRIVATE tonhttp rldp dht tonlib git)
|
target_link_libraries(rldp-http-proxy PRIVATE tonhttp rldp dht tonlib git)
|
||||||
|
|
105
rldp-http-proxy/DNSResolver.cpp
Normal file
105
rldp-http-proxy/DNSResolver.cpp
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
This file is part of TON Blockchain source code.
|
||||||
|
|
||||||
|
TON Blockchain is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU 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 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
#include "DNSResolver.h"
|
||||||
|
#include "td/utils/overloaded.h"
|
||||||
|
|
||||||
|
static const double CACHE_TIMEOUT_HARD = 300.0;
|
||||||
|
static const double CACHE_TIMEOUT_SOFT = 270.0;
|
||||||
|
|
||||||
|
DNSResolver::DNSResolver(td::actor::ActorId<TonlibClient> tonlib_client) : tonlib_client_(std::move(tonlib_client)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNSResolver::start_up() {
|
||||||
|
auto obj = tonlib_api::make_object<tonlib_api::sync>();
|
||||||
|
auto P = td::PromiseCreator::lambda([](td::Result<tonlib_api::object_ptr<tonlib_api::Object>>) {});
|
||||||
|
td::actor::send_closure(tonlib_client_, &TonlibClient::send_request, std::move(obj), std::move(P));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNSResolver::resolve(std::string host, td::Promise<ton::adnl::AdnlNodeIdShort> promise) {
|
||||||
|
auto it = cache_.find(host);
|
||||||
|
if (it != cache_.end()) {
|
||||||
|
const CacheEntry &entry = it->second;
|
||||||
|
double now = td::Time::now();
|
||||||
|
if (now < entry.created_at_ + CACHE_TIMEOUT_HARD) {
|
||||||
|
promise.set_result(entry.id_);
|
||||||
|
promise.reset();
|
||||||
|
if (now < entry.created_at_ + CACHE_TIMEOUT_SOFT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td::Bits256 category = td::sha256_bits256(td::Slice("site", 4));
|
||||||
|
auto obj = tonlib_api::make_object<tonlib_api::dns_resolve>(nullptr, host, category, 16);
|
||||||
|
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), promise = std::move(promise), host = std::move(host)](
|
||||||
|
td::Result<tonlib_api::object_ptr<tonlib_api::Object>> R) mutable {
|
||||||
|
if (R.is_error()) {
|
||||||
|
if (promise) {
|
||||||
|
promise.set_result(R.move_as_error());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto v = R.move_as_ok();
|
||||||
|
auto obj = dynamic_cast<tonlib_api::dns_resolved *>(v.get());
|
||||||
|
if (obj == nullptr) {
|
||||||
|
promise.set_result(td::Status::Error("invalid response from tonlib"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ton::adnl::AdnlNodeIdShort id;
|
||||||
|
td::uint32 cnt = 0;
|
||||||
|
for (auto &e : obj->entries_) {
|
||||||
|
tonlib_api::downcast_call(*e->entry_.get(),
|
||||||
|
td::overloaded(
|
||||||
|
[&](tonlib_api::dns_entryDataAdnlAddress &x) {
|
||||||
|
if (td::Random::fast(0, cnt) == 0) {
|
||||||
|
auto R = ton::adnl::AdnlNodeIdShort::parse(x.adnl_address_->adnl_address_);
|
||||||
|
if (R.is_ok()) {
|
||||||
|
id = R.move_as_ok();
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[&](auto &x) {}));
|
||||||
|
}
|
||||||
|
if (cnt == 0) {
|
||||||
|
if (promise) {
|
||||||
|
promise.set_error(td::Status::Error("no DNS entries"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
td::actor::send_closure(SelfId, &DNSResolver::save_to_cache, std::move(host), id);
|
||||||
|
if (promise) {
|
||||||
|
promise.set_result(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
td::actor::send_closure(tonlib_client_, &TonlibClient::send_request, std::move(obj), std::move(P));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNSResolver::save_to_cache(std::string host, ton::adnl::AdnlNodeIdShort id) {
|
||||||
|
CacheEntry &entry = cache_[host];
|
||||||
|
entry.id_ = id;
|
||||||
|
entry.created_at_ = td::Time::now();
|
||||||
|
}
|
49
rldp-http-proxy/DNSResolver.h
Normal file
49
rldp-http-proxy/DNSResolver.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
This file is part of TON Blockchain source code.
|
||||||
|
|
||||||
|
TON Blockchain is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU 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 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "td/actor/actor.h"
|
||||||
|
#include "TonlibClient.h"
|
||||||
|
#include "adnl/adnl.h"
|
||||||
|
#include "td/actor/PromiseFuture.h"
|
||||||
|
|
||||||
|
class DNSResolver : public td::actor::Actor {
|
||||||
|
public:
|
||||||
|
explicit DNSResolver(td::actor::ActorId<TonlibClient> tonlib_client);
|
||||||
|
|
||||||
|
void start_up() override;
|
||||||
|
void resolve(std::string host, td::Promise<ton::adnl::AdnlNodeIdShort> promise);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void save_to_cache(std::string host, ton::adnl::AdnlNodeIdShort id);
|
||||||
|
|
||||||
|
td::actor::ActorId<TonlibClient> tonlib_client_;
|
||||||
|
|
||||||
|
struct CacheEntry {
|
||||||
|
ton::adnl::AdnlNodeIdShort id_;
|
||||||
|
double created_at_;
|
||||||
|
};
|
||||||
|
std::map<std::string, CacheEntry> cache_;
|
||||||
|
};
|
72
rldp-http-proxy/TonlibClient.cpp
Normal file
72
rldp-http-proxy/TonlibClient.cpp
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
This file is part of TON Blockchain source code.
|
||||||
|
|
||||||
|
TON Blockchain is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU 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 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
#include "TonlibClient.h"
|
||||||
|
|
||||||
|
TonlibClient::TonlibClient(ton::tl_object_ptr<tonlib_api::options> options) : options_(std::move(options)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void TonlibClient::start_up() {
|
||||||
|
class Cb : public tonlib::TonlibCallback {
|
||||||
|
public:
|
||||||
|
explicit Cb(td::actor::ActorId<TonlibClient> self_id) : self_id_(self_id) {
|
||||||
|
}
|
||||||
|
void on_result(std::uint64_t id, tonlib_api::object_ptr<tonlib_api::Object> result) override {
|
||||||
|
td::actor::send_closure(self_id_, &TonlibClient::receive_request_result, id, std::move(result));
|
||||||
|
}
|
||||||
|
void on_error(std::uint64_t id, tonlib_api::object_ptr<tonlib_api::error> error) override {
|
||||||
|
td::actor::send_closure(self_id_, &TonlibClient::receive_request_result, id,
|
||||||
|
td::Status::Error(error->code_, std::move(error->message_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
td::actor::ActorId<TonlibClient> self_id_;
|
||||||
|
};
|
||||||
|
|
||||||
|
tonlib_client_ = td::actor::create_actor<tonlib::TonlibClient>("tonlibclient", td::make_unique<Cb>(actor_id(this)));
|
||||||
|
auto init = tonlib_api::make_object<tonlib_api::init>(std::move(options_));
|
||||||
|
auto P = td::PromiseCreator::lambda([](td::Result<tonlib_api::object_ptr<tonlib_api::Object>> R) mutable {
|
||||||
|
R.ensure();
|
||||||
|
});
|
||||||
|
send_request(std::move(init), std::move(P));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TonlibClient::send_request(tonlib_api::object_ptr<tonlib_api::Function> obj,
|
||||||
|
td::Promise<tonlib_api::object_ptr<tonlib_api::Object>> promise) {
|
||||||
|
auto id = next_request_id_++;
|
||||||
|
CHECK(requests_.emplace(id, std::move(promise)).second);
|
||||||
|
td::actor::send_closure(tonlib_client_, &tonlib::TonlibClient::request, id, std::move(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TonlibClient::receive_request_result(td::uint64 id, td::Result<tonlib_api::object_ptr<tonlib_api::Object>> R) {
|
||||||
|
if (id == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto it = requests_.find(id);
|
||||||
|
CHECK(it != requests_.end());
|
||||||
|
auto promise = std::move(it->second);
|
||||||
|
requests_.erase(it);
|
||||||
|
promise.set_result(std::move(R));
|
||||||
|
}
|
47
rldp-http-proxy/TonlibClient.h
Normal file
47
rldp-http-proxy/TonlibClient.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
This file is part of TON Blockchain source code.
|
||||||
|
|
||||||
|
TON Blockchain is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU 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 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "td/actor/actor.h"
|
||||||
|
#include "auto/tl/tonlib_api.hpp"
|
||||||
|
#include "tonlib/tonlib/TonlibClient.h"
|
||||||
|
|
||||||
|
class TonlibClient : public td::actor::Actor {
|
||||||
|
public:
|
||||||
|
explicit TonlibClient(ton::tl_object_ptr<tonlib_api::options> options);
|
||||||
|
|
||||||
|
void start_up() override;
|
||||||
|
|
||||||
|
void send_request(tonlib_api::object_ptr<tonlib_api::Function> obj,
|
||||||
|
td::Promise<tonlib_api::object_ptr<tonlib_api::Object>> promise);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void receive_request_result(td::uint64 id, td::Result<tonlib_api::object_ptr<tonlib_api::Object>> R);
|
||||||
|
|
||||||
|
ton::tl_object_ptr<tonlib_api::options> options_;
|
||||||
|
td::actor::ActorOwn<tonlib::TonlibClient> tonlib_client_;
|
||||||
|
std::map<td::uint64, td::Promise<tonlib_api::object_ptr<tonlib_api::Object>>> requests_;
|
||||||
|
td::uint64 next_request_id_{1};
|
||||||
|
};
|
|
@ -52,6 +52,11 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include "git.h"
|
#include "git.h"
|
||||||
|
#include "td/utils/BufferedFd.h"
|
||||||
|
#include "common/delay.h"
|
||||||
|
|
||||||
|
#include "TonlibClient.h"
|
||||||
|
#include "DNSResolver.h"
|
||||||
|
|
||||||
#if TD_DARWIN || TD_LINUX
|
#if TD_DARWIN || TD_LINUX
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
@ -120,16 +125,28 @@ class HttpRemote : public td::actor::Actor {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
td::IPAddress addr_;
|
td::IPAddress addr_;
|
||||||
bool ready_ = false;
|
bool ready_ = true;
|
||||||
td::actor::ActorOwn<ton::http::HttpClient> client_;
|
td::actor::ActorOwn<ton::http::HttpClient> client_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
td::BufferSlice create_error_response(const std::string &proto_version, int code, const std::string &reason) {
|
||||||
|
return ton::create_serialize_tl_object<ton::ton_api::http_response>(
|
||||||
|
proto_version, code, reason, std::vector<ton::tl_object_ptr<ton::ton_api::http_header>>(), true);
|
||||||
|
}
|
||||||
|
|
||||||
class HttpRldpPayloadReceiver : public td::actor::Actor {
|
class HttpRldpPayloadReceiver : public td::actor::Actor {
|
||||||
public:
|
public:
|
||||||
HttpRldpPayloadReceiver(std::shared_ptr<ton::http::HttpPayload> payload, td::Bits256 transfer_id,
|
HttpRldpPayloadReceiver(std::shared_ptr<ton::http::HttpPayload> payload, td::Bits256 transfer_id,
|
||||||
ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort local_id,
|
ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort local_id,
|
||||||
td::actor::ActorId<ton::adnl::Adnl> adnl, td::actor::ActorId<ton::rldp::Rldp> rldp)
|
td::actor::ActorId<ton::adnl::Adnl> adnl, td::actor::ActorId<ton::rldp::Rldp> rldp,
|
||||||
: payload_(std::move(payload)), id_(transfer_id), src_(src), local_id_(local_id), adnl_(adnl), rldp_(rldp) {
|
bool is_tunnel = false)
|
||||||
|
: payload_(std::move(payload))
|
||||||
|
, id_(transfer_id)
|
||||||
|
, src_(src)
|
||||||
|
, local_id_(local_id)
|
||||||
|
, adnl_(adnl)
|
||||||
|
, rldp_(rldp)
|
||||||
|
, is_tunnel_(is_tunnel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void start_up() override {
|
void start_up() override {
|
||||||
|
@ -178,13 +195,14 @@ class HttpRldpPayloadReceiver : public td::actor::Actor {
|
||||||
|
|
||||||
auto f = ton::create_serialize_tl_object<ton::ton_api::http_getNextPayloadPart>(
|
auto f = ton::create_serialize_tl_object<ton::ton_api::http_getNextPayloadPart>(
|
||||||
id_, seqno_++, static_cast<td::int32>(chunk_size()));
|
id_, seqno_++, static_cast<td::int32>(chunk_size()));
|
||||||
|
auto timeout = td::Timestamp::in(is_tunnel_ ? 60.0 : 15.0);
|
||||||
td::actor::send_closure(rldp_, &ton::rldp::Rldp::send_query_ex, local_id_, src_, "payload part", std::move(P),
|
td::actor::send_closure(rldp_, &ton::rldp::Rldp::send_query_ex, local_id_, src_, "payload part", std::move(P),
|
||||||
td::Timestamp::in(15.0), std::move(f), 2 * chunk_size() + 1024);
|
timeout, std::move(f), 2 * chunk_size() + 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_data(td::BufferSlice data) {
|
void add_data(td::BufferSlice data) {
|
||||||
LOG(INFO) << "HttpPayloadReceiver: received answer (size " << data.size() << ")";
|
LOG(INFO) << "HttpPayloadReceiver: received answer (size " << data.size() << ")";
|
||||||
auto F = ton::fetch_tl_object<ton::ton_api::http_payloadPart>(std::move(data), true);
|
auto F = ton::fetch_tl_object<ton::ton_api::http_payloadPart>(data, true);
|
||||||
if (F.is_error()) {
|
if (F.is_error()) {
|
||||||
abort_query(F.move_as_error());
|
abort_query(F.move_as_error());
|
||||||
return;
|
return;
|
||||||
|
@ -243,14 +261,20 @@ class HttpRldpPayloadReceiver : public td::actor::Actor {
|
||||||
|
|
||||||
bool sent_ = false;
|
bool sent_ = false;
|
||||||
td::int32 seqno_ = 0;
|
td::int32 seqno_ = 0;
|
||||||
|
bool is_tunnel_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HttpRldpPayloadSender : public td::actor::Actor {
|
class HttpRldpPayloadSender : public td::actor::Actor {
|
||||||
public:
|
public:
|
||||||
HttpRldpPayloadSender(std::shared_ptr<ton::http::HttpPayload> payload, td::Bits256 transfer_id,
|
HttpRldpPayloadSender(std::shared_ptr<ton::http::HttpPayload> payload, td::Bits256 transfer_id,
|
||||||
ton::adnl::AdnlNodeIdShort local_id, td::actor::ActorId<ton::adnl::Adnl> adnl,
|
ton::adnl::AdnlNodeIdShort local_id, td::actor::ActorId<ton::adnl::Adnl> adnl,
|
||||||
td::actor::ActorId<ton::rldp::Rldp> rldp)
|
td::actor::ActorId<ton::rldp::Rldp> rldp, bool is_tunnel = false)
|
||||||
: payload_(std::move(payload)), id_(transfer_id), local_id_(local_id), adnl_(adnl), rldp_(rldp) {
|
: payload_(std::move(payload))
|
||||||
|
, id_(transfer_id)
|
||||||
|
, local_id_(local_id)
|
||||||
|
, adnl_(adnl)
|
||||||
|
, rldp_(rldp)
|
||||||
|
, is_tunnel_(is_tunnel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string generate_prefix() const {
|
std::string generate_prefix() const {
|
||||||
|
@ -287,32 +311,36 @@ class HttpRldpPayloadSender : public td::actor::Actor {
|
||||||
|
|
||||||
class Cb : public ton::http::HttpPayload::Callback {
|
class Cb : public ton::http::HttpPayload::Callback {
|
||||||
public:
|
public:
|
||||||
Cb(td::actor::ActorId<HttpRldpPayloadSender> id) : self_id_(id) {
|
Cb(td::actor::ActorId<HttpRldpPayloadSender> id, size_t watermark) : self_id_(id), watermark_(watermark) {
|
||||||
}
|
}
|
||||||
void run(size_t ready_bytes) override {
|
void run(size_t ready_bytes) override {
|
||||||
if (!reached_ && ready_bytes >= watermark_) {
|
if (!reached_ && ready_bytes >= watermark_) {
|
||||||
reached_ = true;
|
reached_ = true;
|
||||||
td::actor::send_closure(self_id_, &HttpRldpPayloadSender::try_answer_query);
|
td::actor::send_closure(self_id_, &HttpRldpPayloadSender::try_answer_query, false);
|
||||||
} else if (reached_ && ready_bytes < watermark_) {
|
} else if (reached_ && ready_bytes < watermark_) {
|
||||||
reached_ = false;
|
reached_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void completed() override {
|
void completed() override {
|
||||||
td::actor::send_closure(self_id_, &HttpRldpPayloadSender::try_answer_query);
|
td::actor::send_closure(self_id_, &HttpRldpPayloadSender::try_answer_query, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t watermark_ = ton::http::HttpRequest::low_watermark();
|
|
||||||
bool reached_ = false;
|
bool reached_ = false;
|
||||||
td::actor::ActorId<HttpRldpPayloadSender> self_id_;
|
td::actor::ActorId<HttpRldpPayloadSender> self_id_;
|
||||||
|
size_t watermark_;
|
||||||
};
|
};
|
||||||
|
|
||||||
payload_->add_callback(std::make_unique<Cb>(actor_id(this)));
|
payload_->add_callback(
|
||||||
|
std::make_unique<Cb>(actor_id(this), is_tunnel_ ? 1 : ton::http::HttpRequest::low_watermark()));
|
||||||
|
|
||||||
alarm_timestamp() = td::Timestamp::in(10.0);
|
alarm_timestamp() = td::Timestamp::in(is_tunnel_ ? 60.0 : 10.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void try_answer_query() {
|
void try_answer_query(bool from_timer = false) {
|
||||||
|
if (from_timer) {
|
||||||
|
active_timer_ = false;
|
||||||
|
}
|
||||||
if (!cur_query_promise_) {
|
if (!cur_query_promise_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -321,6 +349,17 @@ class HttpRldpPayloadSender : public td::actor::Actor {
|
||||||
}
|
}
|
||||||
if (payload_->parse_completed() || payload_->ready_bytes() >= ton::http::HttpRequest::low_watermark()) {
|
if (payload_->parse_completed() || payload_->ready_bytes() >= ton::http::HttpRequest::low_watermark()) {
|
||||||
answer_query();
|
answer_query();
|
||||||
|
} else if (!is_tunnel_ || payload_->ready_bytes() == 0) {
|
||||||
|
return;
|
||||||
|
} else if (from_timer) {
|
||||||
|
answer_query();
|
||||||
|
} else if (!active_timer_) {
|
||||||
|
active_timer_ = true;
|
||||||
|
ton::delay_action(
|
||||||
|
[SelfId = actor_id(this)]() {
|
||||||
|
td::actor::send_closure(SelfId, &HttpRldpPayloadSender::try_answer_query, true);
|
||||||
|
},
|
||||||
|
td::Timestamp::in(0.001));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,16 +387,12 @@ class HttpRldpPayloadSender : public td::actor::Actor {
|
||||||
LOG(INFO) << "received request. size=" << cur_query_size_ << " parse_completed=" << payload_->parse_completed()
|
LOG(INFO) << "received request. size=" << cur_query_size_ << " parse_completed=" << payload_->parse_completed()
|
||||||
<< " ready_bytes=" << payload_->ready_bytes();
|
<< " ready_bytes=" << payload_->ready_bytes();
|
||||||
|
|
||||||
if (payload_->parse_completed() || payload_->ready_bytes() >= ton::http::HttpRequest::low_watermark()) {
|
alarm_timestamp() = td::Timestamp::in(is_tunnel_ ? 50.0 : 10.0);
|
||||||
answer_query();
|
try_answer_query(false);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
alarm_timestamp() = td::Timestamp::in(10.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_query(td::BufferSlice data, td::Promise<td::BufferSlice> promise) {
|
void receive_query(td::BufferSlice data, td::Promise<td::BufferSlice> promise) {
|
||||||
auto F = ton::fetch_tl_object<ton::ton_api::http_getNextPayloadPart>(std::move(data), true);
|
auto F = ton::fetch_tl_object<ton::ton_api::http_getNextPayloadPart>(data, true);
|
||||||
if (F.is_error()) {
|
if (F.is_error()) {
|
||||||
LOG(INFO) << "failed to parse query: " << F.move_as_error();
|
LOG(INFO) << "failed to parse query: " << F.move_as_error();
|
||||||
return;
|
return;
|
||||||
|
@ -367,6 +402,10 @@ class HttpRldpPayloadSender : public td::actor::Actor {
|
||||||
|
|
||||||
void alarm() override {
|
void alarm() override {
|
||||||
if (cur_query_promise_) {
|
if (cur_query_promise_) {
|
||||||
|
if (is_tunnel_) {
|
||||||
|
answer_query();
|
||||||
|
return;
|
||||||
|
}
|
||||||
LOG(INFO) << "timeout on inbound connection. closing http transfer";
|
LOG(INFO) << "timeout on inbound connection. closing http transfer";
|
||||||
} else {
|
} else {
|
||||||
LOG(INFO) << "timeout on RLDP connection. closing http transfer";
|
LOG(INFO) << "timeout on RLDP connection. closing http transfer";
|
||||||
|
@ -382,7 +421,7 @@ class HttpRldpPayloadSender : public td::actor::Actor {
|
||||||
}
|
}
|
||||||
seqno_++;
|
seqno_++;
|
||||||
|
|
||||||
alarm_timestamp() = td::Timestamp::in(30.0);
|
alarm_timestamp() = td::Timestamp::in(is_tunnel_ ? 60.0 : 30.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void abort_query(td::Status error) {
|
void abort_query(td::Status error) {
|
||||||
|
@ -403,7 +442,6 @@ class HttpRldpPayloadSender : public td::actor::Actor {
|
||||||
|
|
||||||
td::Bits256 id_;
|
td::Bits256 id_;
|
||||||
|
|
||||||
bool sent_ = false;
|
|
||||||
td::int32 seqno_ = 0;
|
td::int32 seqno_ = 0;
|
||||||
|
|
||||||
ton::adnl::AdnlNodeIdShort local_id_;
|
ton::adnl::AdnlNodeIdShort local_id_;
|
||||||
|
@ -412,6 +450,7 @@ class HttpRldpPayloadSender : public td::actor::Actor {
|
||||||
|
|
||||||
size_t cur_query_size_;
|
size_t cur_query_size_;
|
||||||
td::Promise<td::BufferSlice> cur_query_promise_;
|
td::Promise<td::BufferSlice> cur_query_promise_;
|
||||||
|
bool is_tunnel_, active_timer_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RldpHttpProxy;
|
class RldpHttpProxy;
|
||||||
|
@ -423,7 +462,7 @@ class TcpToRldpRequestSender : public td::actor::Actor {
|
||||||
std::shared_ptr<ton::http::HttpPayload> request_payload,
|
std::shared_ptr<ton::http::HttpPayload> request_payload,
|
||||||
td::Promise<std::pair<std::unique_ptr<ton::http::HttpResponse>, std::shared_ptr<ton::http::HttpPayload>>> promise,
|
td::Promise<std::pair<std::unique_ptr<ton::http::HttpResponse>, std::shared_ptr<ton::http::HttpPayload>>> promise,
|
||||||
td::actor::ActorId<ton::adnl::Adnl> adnl, td::actor::ActorId<ton::dht::Dht> dht,
|
td::actor::ActorId<ton::adnl::Adnl> adnl, td::actor::ActorId<ton::dht::Dht> dht,
|
||||||
td::actor::ActorId<ton::rldp::Rldp> rldp, td::actor::ActorId<RldpHttpProxy> proxy)
|
td::actor::ActorId<ton::rldp::Rldp> rldp, td::actor::ActorId<DNSResolver> dns_resolver)
|
||||||
: local_id_(local_id)
|
: local_id_(local_id)
|
||||||
, host_(std::move(host))
|
, host_(std::move(host))
|
||||||
, request_(std::move(request))
|
, request_(std::move(request))
|
||||||
|
@ -432,7 +471,7 @@ class TcpToRldpRequestSender : public td::actor::Actor {
|
||||||
, adnl_(adnl)
|
, adnl_(adnl)
|
||||||
, dht_(dht)
|
, dht_(dht)
|
||||||
, rldp_(rldp)
|
, rldp_(rldp)
|
||||||
, proxy_(proxy) {
|
, dns_resolver_(dns_resolver) {
|
||||||
}
|
}
|
||||||
void start_up() override {
|
void start_up() override {
|
||||||
resolve();
|
resolve();
|
||||||
|
@ -452,7 +491,8 @@ class TcpToRldpRequestSender : public td::actor::Actor {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
td::actor::create_actor<HttpRldpPayloadSender>("HttpPayloadSender", request_payload_, id_, local_id_, adnl_, rldp_)
|
td::actor::create_actor<HttpRldpPayloadSender>("HttpPayloadSender", request_payload_, id_, local_id_, adnl_, rldp_,
|
||||||
|
is_tunnel())
|
||||||
.release();
|
.release();
|
||||||
|
|
||||||
auto f = ton::serialize_tl_object(request_->store_tl(id_), true);
|
auto f = ton::serialize_tl_object(request_->store_tl(id_), true);
|
||||||
|
@ -461,13 +501,14 @@ class TcpToRldpRequestSender : public td::actor::Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
void got_result(td::BufferSlice data) {
|
void got_result(td::BufferSlice data) {
|
||||||
auto F = ton::fetch_tl_object<ton::ton_api::http_response>(std::move(data), true);
|
auto F = ton::fetch_tl_object<ton::ton_api::http_response>(data, true);
|
||||||
if (F.is_error()) {
|
if (F.is_error()) {
|
||||||
abort_query(F.move_as_error());
|
abort_query(F.move_as_error());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto f = F.move_as_ok();
|
auto f = F.move_as_ok();
|
||||||
auto R = ton::http::HttpResponse::create(f->http_version_, f->status_code_, f->reason_, false, true);
|
auto R = ton::http::HttpResponse::create(f->http_version_, f->status_code_, f->reason_, f->no_payload_, true,
|
||||||
|
is_tunnel() && f->status_code_ == 200);
|
||||||
if (R.is_error()) {
|
if (R.is_error()) {
|
||||||
abort_query(R.move_as_error());
|
abort_query(R.move_as_error());
|
||||||
return;
|
return;
|
||||||
|
@ -497,9 +538,13 @@ class TcpToRldpRequestSender : public td::actor::Actor {
|
||||||
td::actor::send_closure(SelfId, &TcpToRldpRequestSender::finished_payload_transfer);
|
td::actor::send_closure(SelfId, &TcpToRldpRequestSender::finished_payload_transfer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (f->no_payload_) {
|
||||||
|
response_payload_->complete_parse();
|
||||||
|
} else {
|
||||||
td::actor::create_actor<HttpRldpPayloadReceiver>("HttpPayloadReceiver", response_payload_, id_, dst_, local_id_,
|
td::actor::create_actor<HttpRldpPayloadReceiver>("HttpPayloadReceiver", response_payload_, id_, dst_, local_id_,
|
||||||
adnl_, rldp_)
|
adnl_, rldp_, is_tunnel())
|
||||||
.release();
|
.release();
|
||||||
|
}
|
||||||
|
|
||||||
promise_.set_value(std::make_pair(std::move(response_), std::move(response_payload_)));
|
promise_.set_value(std::make_pair(std::move(response_), std::move(response_payload_)));
|
||||||
stop();
|
stop();
|
||||||
|
@ -511,10 +556,15 @@ class TcpToRldpRequestSender : public td::actor::Actor {
|
||||||
|
|
||||||
void abort_query(td::Status error) {
|
void abort_query(td::Status error) {
|
||||||
LOG(INFO) << "aborting http over rldp query: " << error;
|
LOG(INFO) << "aborting http over rldp query: " << error;
|
||||||
|
promise_.set_error(std::move(error));
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
bool is_tunnel() const {
|
||||||
|
return request_->method() == "CONNECT";
|
||||||
|
}
|
||||||
|
|
||||||
td::Bits256 id_;
|
td::Bits256 id_;
|
||||||
|
|
||||||
ton::adnl::AdnlNodeIdShort local_id_;
|
ton::adnl::AdnlNodeIdShort local_id_;
|
||||||
|
@ -529,12 +579,216 @@ class TcpToRldpRequestSender : public td::actor::Actor {
|
||||||
td::actor::ActorId<ton::adnl::Adnl> adnl_;
|
td::actor::ActorId<ton::adnl::Adnl> adnl_;
|
||||||
td::actor::ActorId<ton::dht::Dht> dht_;
|
td::actor::ActorId<ton::dht::Dht> dht_;
|
||||||
td::actor::ActorId<ton::rldp::Rldp> rldp_;
|
td::actor::ActorId<ton::rldp::Rldp> rldp_;
|
||||||
td::actor::ActorId<RldpHttpProxy> proxy_;
|
td::actor::ActorId<DNSResolver> dns_resolver_;
|
||||||
|
|
||||||
std::unique_ptr<ton::http::HttpResponse> response_;
|
std::unique_ptr<ton::http::HttpResponse> response_;
|
||||||
std::shared_ptr<ton::http::HttpPayload> response_payload_;
|
std::shared_ptr<ton::http::HttpPayload> response_payload_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class RldpTcpTunnel : public td::actor::Actor, private td::ObserverBase {
|
||||||
|
public:
|
||||||
|
RldpTcpTunnel(td::Bits256 transfer_id, ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort local_id,
|
||||||
|
td::actor::ActorId<ton::adnl::Adnl> adnl, td::actor::ActorId<ton::rldp::Rldp> rldp, td::SocketFd fd)
|
||||||
|
: id_(transfer_id)
|
||||||
|
, src_(src)
|
||||||
|
, local_id_(local_id)
|
||||||
|
, adnl_(std::move(adnl))
|
||||||
|
, rldp_(std::move(rldp))
|
||||||
|
, fd_(std::move(fd)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_up() override {
|
||||||
|
self_ = actor_id(this);
|
||||||
|
td::actor::SchedulerContext::get()->get_poll().subscribe(fd_.get_poll_info().extract_pollable_fd(this),
|
||||||
|
td::PollFlags::ReadWrite());
|
||||||
|
|
||||||
|
class Cb : public ton::adnl::Adnl::Callback {
|
||||||
|
public:
|
||||||
|
explicit Cb(td::actor::ActorId<RldpTcpTunnel> id) : self_id_(std::move(id)) {
|
||||||
|
}
|
||||||
|
void receive_message(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst,
|
||||||
|
td::BufferSlice data) override {
|
||||||
|
LOG(INFO) << "rldp tcp tunnel: dropping message";
|
||||||
|
}
|
||||||
|
void receive_query(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data,
|
||||||
|
td::Promise<td::BufferSlice> promise) override {
|
||||||
|
td::actor::send_closure(self_id_, &RldpTcpTunnel::receive_query, std::move(data), std::move(promise));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
td::actor::ActorId<RldpTcpTunnel> self_id_;
|
||||||
|
};
|
||||||
|
td::actor::send_closure(adnl_, &ton::adnl::Adnl::subscribe, local_id_, generate_prefix(),
|
||||||
|
std::make_unique<Cb>(actor_id(this)));
|
||||||
|
process();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tear_down() override {
|
||||||
|
LOG(INFO) << "RldpTcpTunnel: tear_down";
|
||||||
|
td::actor::send_closure(adnl_, &ton::adnl::Adnl::unsubscribe, local_id_, generate_prefix());
|
||||||
|
td::actor::SchedulerContext::get()->get_poll().unsubscribe(fd_.get_poll_info().get_pollable_fd_ref());
|
||||||
|
}
|
||||||
|
|
||||||
|
void notify() override {
|
||||||
|
td::actor::send_closure(self_, &RldpTcpTunnel::process);
|
||||||
|
}
|
||||||
|
|
||||||
|
void request_data() {
|
||||||
|
if (close_ || sent_request_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sent_request_ = true;
|
||||||
|
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
|
||||||
|
td::actor::send_closure(SelfId, &RldpTcpTunnel::got_data_from_rldp, std::move(R));
|
||||||
|
});
|
||||||
|
|
||||||
|
auto f = ton::create_serialize_tl_object<ton::ton_api::http_getNextPayloadPart>(id_, out_seqno_++, 1 << 17);
|
||||||
|
td::actor::send_closure(rldp_, &ton::rldp::Rldp::send_query_ex, local_id_, src_, "payload part", std::move(P),
|
||||||
|
td::Timestamp::in(60.0), std::move(f), (1 << 18) + 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
void receive_query(td::BufferSlice data, td::Promise<td::BufferSlice> promise) {
|
||||||
|
auto F = ton::fetch_tl_object<ton::ton_api::http_getNextPayloadPart>(data, true);
|
||||||
|
if (F.is_error()) {
|
||||||
|
LOG(INFO) << "failed to parse query: " << F.error();
|
||||||
|
promise.set_error(F.move_as_error());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto f = F.move_as_ok();
|
||||||
|
if (cur_promise_) {
|
||||||
|
LOG(INFO) << "failed to process query: previous query is active";
|
||||||
|
promise.set_error(td::Status::Error("previous query is active"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (f->seqno_ != cur_seqno_) {
|
||||||
|
LOG(INFO) << "failed to process query: seqno mismatch";
|
||||||
|
promise.set_error(td::Status::Error("seqno mismatch"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOG(INFO) << "RldpTcpTunnel: received query, seqno=" << cur_seqno_;
|
||||||
|
cur_promise_ = std::move(promise);
|
||||||
|
cur_max_chunk_size_ = f->max_chunk_size_;
|
||||||
|
alarm_timestamp() = td::Timestamp::in(50.0);
|
||||||
|
process();
|
||||||
|
}
|
||||||
|
|
||||||
|
void got_data_from_rldp(td::Result<td::BufferSlice> R) {
|
||||||
|
if (R.is_error()) {
|
||||||
|
abort(R.move_as_error());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
td::BufferSlice data = R.move_as_ok();
|
||||||
|
LOG(INFO) << "RldpTcpTunnel: received data from rldp: size=" << data.size();
|
||||||
|
sent_request_ = false;
|
||||||
|
auto F = ton::fetch_tl_object<ton::ton_api::http_payloadPart>(data, true);
|
||||||
|
if (F.is_error()) {
|
||||||
|
abort(F.move_as_error());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto f = F.move_as_ok();
|
||||||
|
fd_.output_buffer().append(std::move(f->data_));
|
||||||
|
if (f->last_) {
|
||||||
|
got_last_part_ = true;
|
||||||
|
}
|
||||||
|
process();
|
||||||
|
}
|
||||||
|
|
||||||
|
void process() {
|
||||||
|
if (!close_) {
|
||||||
|
auto status = [&] {
|
||||||
|
TRY_STATUS(fd_.flush_read());
|
||||||
|
TRY_STATUS(fd_.flush_write());
|
||||||
|
close_ = td::can_close(fd_);
|
||||||
|
return td::Status::OK();
|
||||||
|
}();
|
||||||
|
if (status.is_error()) {
|
||||||
|
abort(std::move(status));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (got_last_part_) {
|
||||||
|
close_ = true;
|
||||||
|
}
|
||||||
|
answer_query();
|
||||||
|
request_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void answer_query(bool allow_empty = false, bool from_timer = false) {
|
||||||
|
if (from_timer) {
|
||||||
|
active_timer_ = false;
|
||||||
|
}
|
||||||
|
auto &input = fd_.input_buffer();
|
||||||
|
if (cur_promise_ && (!input.empty() || close_ || allow_empty)) {
|
||||||
|
if (!from_timer && !close_ && !allow_empty && input.size() < ton::http::HttpRequest::low_watermark()) {
|
||||||
|
if (!active_timer_) {
|
||||||
|
active_timer_ = true;
|
||||||
|
ton::delay_action(
|
||||||
|
[SelfId = actor_id(this)]() {
|
||||||
|
td::actor::send_closure(SelfId, &RldpTcpTunnel::answer_query, false, true);
|
||||||
|
},
|
||||||
|
td::Timestamp::in(0.001));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t s = std::min<size_t>(input.size(), cur_max_chunk_size_);
|
||||||
|
td::BufferSlice data(s);
|
||||||
|
LOG(INFO) << "RldpTcpTunnel: sending data to rldp: size=" << data.size();
|
||||||
|
input.advance(s, td::as_mutable_slice(data));
|
||||||
|
cur_promise_.set_result(ton::create_serialize_tl_object<ton::ton_api::http_payloadPart>(
|
||||||
|
std::move(data), std::vector<ton::tl_object_ptr<ton::ton_api::http_header>>(), close_));
|
||||||
|
++cur_seqno_;
|
||||||
|
cur_promise_.reset();
|
||||||
|
alarm_timestamp() = td::Timestamp::never();
|
||||||
|
if (close_) {
|
||||||
|
stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void alarm() override {
|
||||||
|
answer_query(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void abort(td::Status status) {
|
||||||
|
LOG(INFO) << "RldpTcpTunnel error: " << status;
|
||||||
|
if (cur_promise_) {
|
||||||
|
cur_promise_.set_error(status.move_as_error());
|
||||||
|
}
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string generate_prefix() const {
|
||||||
|
std::string x(static_cast<size_t>(36), '\0');
|
||||||
|
auto S = td::MutableSlice{x};
|
||||||
|
CHECK(S.size() == 36);
|
||||||
|
|
||||||
|
auto id = ton::ton_api::http_getNextPayloadPart::ID;
|
||||||
|
S.copy_from(td::Slice(reinterpret_cast<const td::uint8 *>(&id), 4));
|
||||||
|
S.remove_prefix(4);
|
||||||
|
S.copy_from(id_.as_slice());
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
td::Bits256 id_;
|
||||||
|
|
||||||
|
ton::adnl::AdnlNodeIdShort src_;
|
||||||
|
ton::adnl::AdnlNodeIdShort local_id_;
|
||||||
|
td::actor::ActorId<ton::adnl::Adnl> adnl_;
|
||||||
|
td::actor::ActorId<ton::rldp::Rldp> rldp_;
|
||||||
|
|
||||||
|
td::BufferedFd<td::SocketFd> fd_;
|
||||||
|
|
||||||
|
td::actor::ActorId<RldpTcpTunnel> self_;
|
||||||
|
|
||||||
|
td::int32 cur_seqno_ = 0, cur_max_chunk_size_ = 0;
|
||||||
|
td::Promise<td::BufferSlice> cur_promise_;
|
||||||
|
td::int32 out_seqno_ = 0;
|
||||||
|
bool close_ = false, sent_request_ = false, got_last_part_ = false;
|
||||||
|
bool active_timer_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
class RldpToTcpRequestSender : public td::actor::Actor {
|
class RldpToTcpRequestSender : public td::actor::Actor {
|
||||||
public:
|
public:
|
||||||
RldpToTcpRequestSender(td::Bits256 id, ton::adnl::AdnlNodeIdShort local_id, ton::adnl::AdnlNodeIdShort dst,
|
RldpToTcpRequestSender(td::Bits256 id, ton::adnl::AdnlNodeIdShort local_id, ton::adnl::AdnlNodeIdShort dst,
|
||||||
|
@ -570,9 +824,11 @@ class RldpToTcpRequestSender : public td::actor::Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
void got_result(std::pair<std::unique_ptr<ton::http::HttpResponse>, std::shared_ptr<ton::http::HttpPayload>> R) {
|
void got_result(std::pair<std::unique_ptr<ton::http::HttpResponse>, std::shared_ptr<ton::http::HttpPayload>> R) {
|
||||||
|
if (R.first->need_payload()) {
|
||||||
td::actor::create_actor<HttpRldpPayloadSender>("HttpPayloadSender(R)", std::move(R.second), id_, local_id_, adnl_,
|
td::actor::create_actor<HttpRldpPayloadSender>("HttpPayloadSender(R)", std::move(R.second), id_, local_id_, adnl_,
|
||||||
rldp_)
|
rldp_)
|
||||||
.release();
|
.release();
|
||||||
|
}
|
||||||
auto f = ton::serialize_tl_object(R.first->store_tl(), true);
|
auto f = ton::serialize_tl_object(R.first->store_tl(), true);
|
||||||
promise_.set_value(std::move(f));
|
promise_.set_value(std::move(f));
|
||||||
stop();
|
stop();
|
||||||
|
@ -580,7 +836,7 @@ class RldpToTcpRequestSender : public td::actor::Actor {
|
||||||
|
|
||||||
void abort_query(td::Status error) {
|
void abort_query(td::Status error) {
|
||||||
LOG(INFO) << "aborting http over rldp query: " << error;
|
LOG(INFO) << "aborting http over rldp query: " << error;
|
||||||
promise_.set_error(std::move(error));
|
promise_.set_result(create_error_response(request_->proto_version(), 502, "Bad Gateway"));
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -603,8 +859,7 @@ class RldpToTcpRequestSender : public td::actor::Actor {
|
||||||
|
|
||||||
class RldpHttpProxy : public td::actor::Actor {
|
class RldpHttpProxy : public td::actor::Actor {
|
||||||
public:
|
public:
|
||||||
RldpHttpProxy() {
|
RldpHttpProxy() = default;
|
||||||
}
|
|
||||||
|
|
||||||
void set_port(td::uint16 port) {
|
void set_port(td::uint16 port) {
|
||||||
if (port_) {
|
if (port_) {
|
||||||
|
@ -627,29 +882,8 @@ class RldpHttpProxy : public td::actor::Actor {
|
||||||
client_port_ = port;
|
client_port_ = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_local_host(std::string name, td::IPAddress remote) {
|
void set_local_host(std::string host, td::uint16 port, td::IPAddress remote) {
|
||||||
local_hosts_.emplace_back(std::move(name), std::move(remote));
|
hosts_[host].ports_[port].remote_addr_ = remote;
|
||||||
}
|
|
||||||
|
|
||||||
void receive_request_result(td::uint64 id, td::Result<tonlib_api::object_ptr<tonlib_api::Object>> R) {
|
|
||||||
if (id == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto it = tonlib_requests_.find(id);
|
|
||||||
CHECK(it != tonlib_requests_.end());
|
|
||||||
auto promise = std::move(it->second);
|
|
||||||
tonlib_requests_.erase(it);
|
|
||||||
|
|
||||||
promise.set_result(std::move(R));
|
|
||||||
}
|
|
||||||
|
|
||||||
void send_tonlib_request(tonlib_api::object_ptr<tonlib_api::Function> obj,
|
|
||||||
td::Promise<tonlib_api::object_ptr<tonlib_api::Object>> promise) {
|
|
||||||
auto id = next_tonlib_requests_id_++;
|
|
||||||
|
|
||||||
CHECK(tonlib_requests_.emplace(id, std::move(promise)).second);
|
|
||||||
|
|
||||||
td::actor::send_closure(tonlib_client_, &tonlib::TonlibClient::request, id, std::move(obj));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
td::Status load_global_config() {
|
td::Status load_global_config() {
|
||||||
|
@ -666,29 +900,11 @@ class RldpHttpProxy : public td::actor::Actor {
|
||||||
TRY_RESULT_PREFIX(dht, ton::dht::Dht::create_global_config(std::move(conf.dht_)), "bad [dht] section: ");
|
TRY_RESULT_PREFIX(dht, ton::dht::Dht::create_global_config(std::move(conf.dht_)), "bad [dht] section: ");
|
||||||
dht_config_ = std::move(dht);
|
dht_config_ = std::move(dht);
|
||||||
|
|
||||||
class Cb : public tonlib::TonlibCallback {
|
|
||||||
public:
|
|
||||||
Cb(td::actor::ActorId<RldpHttpProxy> self_id) : self_id_(self_id) {
|
|
||||||
}
|
|
||||||
void on_result(std::uint64_t id, tonlib_api::object_ptr<tonlib_api::Object> result) override {
|
|
||||||
td::actor::send_closure(self_id_, &RldpHttpProxy::receive_request_result, id, std::move(result));
|
|
||||||
}
|
|
||||||
void on_error(std::uint64_t id, tonlib_api::object_ptr<tonlib_api::error> error) override {
|
|
||||||
td::actor::send_closure(self_id_, &RldpHttpProxy::receive_request_result, id,
|
|
||||||
td::Status::Error(error->code_, std::move(error->message_)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
td::actor::ActorId<RldpHttpProxy> self_id_;
|
|
||||||
};
|
|
||||||
|
|
||||||
tonlib_client_ = td::actor::create_actor<tonlib::TonlibClient>("tonlibclient", td::make_unique<Cb>(actor_id(this)));
|
|
||||||
|
|
||||||
return td::Status::OK();
|
return td::Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
void store_dht() {
|
void store_dht() {
|
||||||
for (auto &serv : local_hosts_) {
|
for (auto &serv : hosts_) {
|
||||||
if (serv.first != "*") {
|
if (serv.first != "*") {
|
||||||
for (auto &serv_id : server_ids_) {
|
for (auto &serv_id : server_ids_) {
|
||||||
ton::PublicKey key = ton::pubkeys::Unenc{"http." + serv.first};
|
ton::PublicKey key = ton::pubkeys::Unenc{"http." + serv.first};
|
||||||
|
@ -722,7 +938,7 @@ class RldpHttpProxy : public td::actor::Actor {
|
||||||
{
|
{
|
||||||
auto S = load_global_config();
|
auto S = load_global_config();
|
||||||
if (S.is_error()) {
|
if (S.is_error()) {
|
||||||
LOG(INFO) << S;
|
LOG(ERROR) << S;
|
||||||
std::_Exit(2);
|
std::_Exit(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -749,23 +965,19 @@ class RldpHttpProxy : public td::actor::Actor {
|
||||||
});
|
});
|
||||||
td::actor::send_closure(keyring_, &ton::keyring::Keyring::get_public_key, x.pubkey_hash(), std::move(Q));
|
td::actor::send_closure(keyring_, &ton::keyring::Keyring::get_public_key, x.pubkey_hash(), std::move(Q));
|
||||||
}
|
}
|
||||||
auto Q = td::PromiseCreator::lambda(
|
|
||||||
[promise = ig.get_promise()](td::Result<tonlib_api::object_ptr<tonlib_api::Object>> R) mutable {
|
|
||||||
R.ensure();
|
|
||||||
promise.set_value(td::Unit());
|
|
||||||
});
|
|
||||||
|
|
||||||
auto conf_dataR = td::read_file(global_config_);
|
auto conf_dataR = td::read_file(global_config_);
|
||||||
conf_dataR.ensure();
|
conf_dataR.ensure();
|
||||||
|
|
||||||
auto req = tonlib_api::make_object<tonlib_api::init>(tonlib_api::make_object<tonlib_api::options>(
|
auto tonlib_options = tonlib_api::make_object<tonlib_api::options>(
|
||||||
tonlib_api::make_object<tonlib_api::config>(conf_dataR.move_as_ok().as_slice().str(), "", false, false),
|
tonlib_api::make_object<tonlib_api::config>(conf_dataR.move_as_ok().as_slice().str(), "", false, false),
|
||||||
tonlib_api::make_object<tonlib_api::keyStoreTypeInMemory>()));
|
tonlib_api::make_object<tonlib_api::keyStoreTypeInMemory>());
|
||||||
send_tonlib_request(std::move(req), std::move(Q));
|
tonlib_client_ = td::actor::create_actor<TonlibClient>("tonlibclient", std::move(tonlib_options));
|
||||||
|
dns_resolver_ = td::actor::create_actor<DNSResolver>("dnsresolver", tonlib_client_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void run_cont() {
|
void run_cont() {
|
||||||
if (is_client_ && local_hosts_.size() > 0) {
|
if (is_client_ && hosts_.size() > 0) {
|
||||||
LOG(ERROR) << "client-only node cannot be server";
|
LOG(ERROR) << "client-only node cannot be server";
|
||||||
std::_Exit(2);
|
std::_Exit(2);
|
||||||
}
|
}
|
||||||
|
@ -876,9 +1088,6 @@ class RldpHttpProxy : public td::actor::Actor {
|
||||||
ton::adnl::Adnl::int_to_bytestring(ton::ton_api::http_request::ID),
|
ton::adnl::Adnl::int_to_bytestring(ton::ton_api::http_request::ID),
|
||||||
std::make_unique<AdnlCb>(actor_id(this)));
|
std::make_unique<AdnlCb>(actor_id(this)));
|
||||||
}
|
}
|
||||||
for (auto &serv : local_hosts_) {
|
|
||||||
servers_.emplace(serv.first, td::actor::create_actor<HttpRemote>("remote", serv.second));
|
|
||||||
}
|
|
||||||
|
|
||||||
rldp_ = ton::rldp::Rldp::create(adnl_.get());
|
rldp_ = ton::rldp::Rldp::create(adnl_.get());
|
||||||
td::actor::send_closure(rldp_, &ton::rldp::Rldp::add_id, local_id_);
|
td::actor::send_closure(rldp_, &ton::rldp::Rldp::add_id, local_id_);
|
||||||
|
@ -931,23 +1140,33 @@ class RldpHttpProxy : public td::actor::Actor {
|
||||||
|
|
||||||
td::actor::create_actor<TcpToRldpRequestSender>("outboundreq", local_id_, host, std::move(request),
|
td::actor::create_actor<TcpToRldpRequestSender>("outboundreq", local_id_, host, std::move(request),
|
||||||
std::move(payload), std::move(promise), adnl_.get(), dht_.get(),
|
std::move(payload), std::move(promise), adnl_.get(), dht_.get(),
|
||||||
rldp_.get(), actor_id(this))
|
rldp_.get(), dns_resolver_.get())
|
||||||
.release();
|
.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_rldp_request(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data,
|
void receive_rldp_request(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data,
|
||||||
td::Promise<td::BufferSlice> promise) {
|
td::Promise<td::BufferSlice> promise) {
|
||||||
LOG(INFO) << "got HTTP request over rldp from " << src;
|
LOG(INFO) << "got HTTP request over rldp from " << src;
|
||||||
TRY_RESULT_PROMISE(promise, f, ton::fetch_tl_object<ton::ton_api::http_request>(std::move(data), true));
|
TRY_RESULT_PROMISE(promise, f, ton::fetch_tl_object<ton::ton_api::http_request>(data, true));
|
||||||
TRY_RESULT_PROMISE(promise, request, ton::http::HttpRequest::create(f->method_, f->url_, f->http_version_));
|
std::unique_ptr<ton::http::HttpRequest> request;
|
||||||
|
auto S = [&]() {
|
||||||
|
TRY_RESULT_ASSIGN(request, ton::http::HttpRequest::create(f->method_, f->url_, f->http_version_));
|
||||||
for (auto &x : f->headers_) {
|
for (auto &x : f->headers_) {
|
||||||
ton::http::HttpHeader h{x->name_, x->value_};
|
ton::http::HttpHeader h{x->name_, x->value_};
|
||||||
TRY_STATUS_PROMISE(promise, h.basic_check());
|
TRY_STATUS(h.basic_check());
|
||||||
request->add_header(std::move(h));
|
request->add_header(std::move(h));
|
||||||
}
|
}
|
||||||
TRY_STATUS_PROMISE(promise, request->complete_parse_header());
|
TRY_STATUS(request->complete_parse_header());
|
||||||
|
return td::Status::OK();
|
||||||
|
}();
|
||||||
|
if (S.is_error()) {
|
||||||
|
LOG(INFO) << "Failed to parse http request: " << S;
|
||||||
|
promise.set_result(create_error_response(f->http_version_, 400, "Bad Request"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto host = request->host();
|
auto host = request->host();
|
||||||
if (host.size() == 0) {
|
td::uint16 port = 80;
|
||||||
|
if (host.empty()) {
|
||||||
host = request->url();
|
host = request->url();
|
||||||
if (host.size() >= 7 && host.substr(0, 7) == "http://") {
|
if (host.size() >= 7 && host.substr(0, 7) == "http://") {
|
||||||
host = host.substr(7);
|
host = host.substr(7);
|
||||||
|
@ -972,29 +1191,69 @@ class RldpHttpProxy : public td::actor::Actor {
|
||||||
{
|
{
|
||||||
auto p = host.find(':');
|
auto p = host.find(':');
|
||||||
if (p != std::string::npos) {
|
if (p != std::string::npos) {
|
||||||
|
try {
|
||||||
|
port = (td::uint16)std::stoul(host.substr(p + 1));
|
||||||
|
} catch (const std::logic_error &) {
|
||||||
|
port = 80;
|
||||||
|
promise.set_result(create_error_response(f->http_version_, 400, "Bad Request"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
host = host.substr(0, p);
|
host = host.substr(0, p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::transform(host.begin(), host.end(), host.begin(), [](unsigned char c) { return std::tolower(c); });
|
std::transform(host.begin(), host.end(), host.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||||
|
|
||||||
auto it = servers_.find(host);
|
auto it = hosts_.find(host);
|
||||||
if (it == servers_.end()) {
|
if (it == hosts_.end()) {
|
||||||
it = servers_.find("*");
|
it = hosts_.find("*");
|
||||||
if (it == servers_.end()) {
|
if (it == hosts_.end()) {
|
||||||
promise.set_error(td::Status::Error(ton::ErrorCode::error, "unknown server name"));
|
promise.set_result(create_error_response(f->http_version_, 502, "Bad Gateway"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
auto it2 = it->second.ports_.find(port);
|
||||||
|
if (it2 == it->second.ports_.end()) {
|
||||||
|
promise.set_result(create_error_response(f->http_version_, 502, "Bad Gateway"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &server = it2->second;
|
||||||
|
if (request->method() == "CONNECT") {
|
||||||
|
LOG(INFO) << "starting HTTP tunnel over RLDP to " << server.remote_addr_;
|
||||||
|
start_tcp_tunnel(f->id_, src, dst, f->http_version_, server.remote_addr_, std::move(promise));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
TRY_RESULT_PROMISE(promise, payload, request->create_empty_payload());
|
if (server.http_remote_.empty()) {
|
||||||
|
server.http_remote_ = td::actor::create_actor<HttpRemote>("remote", server.remote_addr_);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto payload = request->create_empty_payload();
|
||||||
|
if (payload.is_error()) {
|
||||||
|
promise.set_result(create_error_response(f->http_version_, 502, "Bad Gateway"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
LOG(INFO) << "starting HTTP over RLDP request";
|
LOG(INFO) << "starting HTTP over RLDP request";
|
||||||
td::actor::create_actor<RldpToTcpRequestSender>("inboundreq", f->id_, dst, src, std::move(request),
|
td::actor::create_actor<RldpToTcpRequestSender>("inboundreq", f->id_, dst, src, std::move(request),
|
||||||
std::move(payload), std::move(promise), adnl_.get(), rldp_.get(),
|
payload.move_as_ok(), std::move(promise), adnl_.get(), rldp_.get(),
|
||||||
it->second.get())
|
server.http_remote_.get())
|
||||||
.release();
|
.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void start_tcp_tunnel(td::Bits256 id, ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort local_id,
|
||||||
|
std::string http_version, td::IPAddress ip, td::Promise<td::BufferSlice> promise) {
|
||||||
|
auto fd = td::SocketFd::open(ip);
|
||||||
|
if (fd.is_error()) {
|
||||||
|
promise.set_result(create_error_response(http_version, 502, "Bad Gateway"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
td::actor::create_actor<RldpTcpTunnel>(td::actor::ActorOptions().with_name("tunnel").with_poll(), id, src, local_id,
|
||||||
|
adnl_.get(), rldp_.get(), fd.move_as_ok()).release();
|
||||||
|
promise.set_result(ton::create_serialize_tl_object<ton::ton_api::http_response>(
|
||||||
|
http_version, 200, "Connection Established", std::vector<ton::tl_object_ptr<ton::ton_api::http_header>>(),
|
||||||
|
false));
|
||||||
|
}
|
||||||
|
|
||||||
void add_adnl_addr(ton::adnl::AdnlNodeIdShort id) {
|
void add_adnl_addr(ton::adnl::AdnlNodeIdShort id) {
|
||||||
server_ids_.insert(id);
|
server_ids_.insert(id);
|
||||||
}
|
}
|
||||||
|
@ -1008,10 +1267,17 @@ class RldpHttpProxy : public td::actor::Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct Host {
|
||||||
|
struct Server {
|
||||||
|
td::IPAddress remote_addr_;
|
||||||
|
td::actor::ActorOwn<HttpRemote> http_remote_;
|
||||||
|
};
|
||||||
|
std::map<td::uint16, Server> ports_;
|
||||||
|
};
|
||||||
|
|
||||||
td::uint16 port_{0};
|
td::uint16 port_{0};
|
||||||
td::IPAddress addr_;
|
td::IPAddress addr_;
|
||||||
std::string global_config_;
|
std::string global_config_;
|
||||||
std::vector<std::pair<std::string, td::IPAddress>> local_hosts_;
|
|
||||||
|
|
||||||
bool is_client_{false};
|
bool is_client_{false};
|
||||||
td::uint16 client_port_{0};
|
td::uint16 client_port_{0};
|
||||||
|
@ -1022,8 +1288,7 @@ class RldpHttpProxy : public td::actor::Actor {
|
||||||
ton::adnl::AdnlNodeIdShort dht_id_;
|
ton::adnl::AdnlNodeIdShort dht_id_;
|
||||||
|
|
||||||
td::actor::ActorOwn<ton::http::HttpServer> server_;
|
td::actor::ActorOwn<ton::http::HttpServer> server_;
|
||||||
std::map<std::string, ton::adnl::AdnlNodeIdShort> dns_;
|
std::map<std::string, Host> hosts_;
|
||||||
std::map<std::string, td::actor::ActorOwn<HttpRemote>> servers_;
|
|
||||||
|
|
||||||
td::actor::ActorOwn<ton::keyring::Keyring> keyring_;
|
td::actor::ActorOwn<ton::keyring::Keyring> keyring_;
|
||||||
td::actor::ActorOwn<ton::adnl::AdnlNetworkManager> adnl_network_manager_;
|
td::actor::ActorOwn<ton::adnl::AdnlNetworkManager> adnl_network_manager_;
|
||||||
|
@ -1036,9 +1301,8 @@ class RldpHttpProxy : public td::actor::Actor {
|
||||||
std::string db_root_ = ".";
|
std::string db_root_ = ".";
|
||||||
bool proxy_all_ = false;
|
bool proxy_all_ = false;
|
||||||
|
|
||||||
td::actor::ActorOwn<tonlib::TonlibClient> tonlib_client_;
|
td::actor::ActorOwn<TonlibClient> tonlib_client_;
|
||||||
std::map<td::uint64, td::Promise<tonlib_api::object_ptr<tonlib_api::Object>>> tonlib_requests_;
|
td::actor::ActorOwn<DNSResolver> dns_resolver_;
|
||||||
td::uint64 next_tonlib_requests_id_{1};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void TcpToRldpRequestSender::resolve() {
|
void TcpToRldpRequestSender::resolve() {
|
||||||
|
@ -1053,63 +1317,15 @@ void TcpToRldpRequestSender::resolve() {
|
||||||
resolved(R.move_as_ok());
|
resolved(R.move_as_ok());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (false) {
|
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<ton::adnl::AdnlNodeIdShort> R) {
|
||||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<ton::dht::DhtValue> R) {
|
|
||||||
if (R.is_error()) {
|
|
||||||
td::actor::send_closure(SelfId, &TcpToRldpRequestSender::abort_query, R.move_as_error());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto value = R.move_as_ok();
|
|
||||||
if (value.value().size() != 32) {
|
|
||||||
td::actor::send_closure(SelfId, &TcpToRldpRequestSender::abort_query, td::Status::Error("bad value in dht"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ton::PublicKeyHash h{value.value().as_slice()};
|
|
||||||
td::actor::send_closure(SelfId, &TcpToRldpRequestSender::resolved, ton::adnl::AdnlNodeIdShort{h});
|
|
||||||
});
|
|
||||||
|
|
||||||
ton::PublicKey key = ton::pubkeys::Unenc{"http." + host_};
|
|
||||||
ton::dht::DhtKey dht_key{key.compute_short_id(), "http." + host_, 0};
|
|
||||||
td::actor::send_closure(dht_, &ton::dht::Dht::get_value, std::move(dht_key), std::move(P));
|
|
||||||
} else {
|
|
||||||
td::Bits256 category = td::sha256_bits256(td::Slice("site", 4));
|
|
||||||
auto obj = tonlib_api::make_object<tonlib_api::dns_resolve>(nullptr, host_, category, 16);
|
|
||||||
|
|
||||||
auto P =
|
|
||||||
td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<tonlib_api::object_ptr<tonlib_api::Object>> R) {
|
|
||||||
if (R.is_error()) {
|
if (R.is_error()) {
|
||||||
td::actor::send_closure(SelfId, &TcpToRldpRequestSender::abort_query,
|
td::actor::send_closure(SelfId, &TcpToRldpRequestSender::abort_query,
|
||||||
R.move_as_error_prefix("failed to resolve: "));
|
R.move_as_error_prefix("failed to resolve: "));
|
||||||
} else {
|
} else {
|
||||||
auto v = R.move_as_ok();
|
td::actor::send_closure(SelfId, &TcpToRldpRequestSender::resolved, R.move_as_ok());
|
||||||
auto obj = static_cast<tonlib_api::dns_resolved *>(v.get());
|
|
||||||
ton::adnl::AdnlNodeIdShort id;
|
|
||||||
td::uint32 cnt = 0;
|
|
||||||
for (auto &e : obj->entries_) {
|
|
||||||
tonlib_api::downcast_call(
|
|
||||||
*e->entry_.get(), td::overloaded(
|
|
||||||
[&](tonlib_api::dns_entryDataAdnlAddress &x) {
|
|
||||||
if (td::Random::fast(0, cnt) == 0) {
|
|
||||||
auto R = ton::adnl::AdnlNodeIdShort::parse(x.adnl_address_->adnl_address_);
|
|
||||||
if (R.is_ok()) {
|
|
||||||
id = R.move_as_ok();
|
|
||||||
cnt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[&](auto &x) {}));
|
|
||||||
}
|
|
||||||
if (cnt == 0) {
|
|
||||||
td::actor::send_closure(SelfId, &TcpToRldpRequestSender::abort_query,
|
|
||||||
td::Status::Error(ton::ErrorCode::notready, "failed to resolve"));
|
|
||||||
} else {
|
|
||||||
td::actor::send_closure(SelfId, &TcpToRldpRequestSender::resolved, id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
td::actor::send_closure(proxy_, &RldpHttpProxy::send_tonlib_request, std::move(obj), std::move(P));
|
td::actor::send_closure(dns_resolver_, &DNSResolver::resolve, host_, std::move(P));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
@ -1123,6 +1339,42 @@ int main(int argc, char *argv[]) {
|
||||||
td::log_interface = td::default_log_interface;
|
td::log_interface = td::default_log_interface;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto add_local_host = [&](const std::string& local, const std::string& remote) -> td::Status {
|
||||||
|
std::string host;
|
||||||
|
std::vector<td::uint16> ports;
|
||||||
|
auto p = local.find(':');
|
||||||
|
if (p == std::string::npos) {
|
||||||
|
host = local;
|
||||||
|
ports = {80, 443};
|
||||||
|
} else {
|
||||||
|
host = local.substr(0, p);
|
||||||
|
++p;
|
||||||
|
while (p < local.size()) {
|
||||||
|
auto p2 = local.find(',', p);
|
||||||
|
if (p2 == std::string::npos) {
|
||||||
|
p2 = local.size();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ports.push_back((td::uint16)std::stoul(local.substr(p, p2 - p)));
|
||||||
|
} catch (const std::logic_error& e) {
|
||||||
|
return td::Status::Error(PSLICE() << "Invalid port: " << local.substr(p, p2 - p));
|
||||||
|
}
|
||||||
|
p = p2 + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (td::uint16 port : ports) {
|
||||||
|
std::string cur_remote = remote;
|
||||||
|
if (cur_remote.find(':') == std::string::npos) {
|
||||||
|
cur_remote += ':';
|
||||||
|
cur_remote += std::to_string(port);
|
||||||
|
}
|
||||||
|
td::IPAddress addr;
|
||||||
|
TRY_STATUS(addr.init_host_port(cur_remote));
|
||||||
|
td::actor::send_closure(x, &RldpHttpProxy::set_local_host, host, port, addr);
|
||||||
|
}
|
||||||
|
return td::Status::OK();
|
||||||
|
};
|
||||||
|
|
||||||
td::OptionParser p;
|
td::OptionParser p;
|
||||||
p.set_description(
|
p.set_description(
|
||||||
"A simple rldp-to-http and http-to-rldp proxy for running and accessing ton sites\n"
|
"A simple rldp-to-http and http-to-rldp proxy for running and accessing ton sites\n"
|
||||||
|
@ -1136,7 +1388,8 @@ int main(int argc, char *argv[]) {
|
||||||
SET_VERBOSITY_LEVEL(v);
|
SET_VERBOSITY_LEVEL(v);
|
||||||
});
|
});
|
||||||
p.add_option('V', "version", "shows rldp-http-proxy build information", [&]() {
|
p.add_option('V', "version", "shows rldp-http-proxy build information", [&]() {
|
||||||
std::cout << "rldp-http-proxy build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n";
|
std::cout << "rldp-http-proxy build information: [ Commit: " << GitMetadata::CommitSHA1()
|
||||||
|
<< ", Date: " << GitMetadata::CommitDate() << "]\n";
|
||||||
std::exit(0);
|
std::exit(0);
|
||||||
});
|
});
|
||||||
p.add_option('h', "help", "prints a help message", [&]() {
|
p.add_option('h', "help", "prints a help message", [&]() {
|
||||||
|
@ -1170,27 +1423,25 @@ int main(int argc, char *argv[]) {
|
||||||
});
|
});
|
||||||
p.add_option('C', "global-config", "global TON configuration file",
|
p.add_option('C', "global-config", "global TON configuration file",
|
||||||
[&](td::Slice arg) { td::actor::send_closure(x, &RldpHttpProxy::set_global_config, arg.str()); });
|
[&](td::Slice arg) { td::actor::send_closure(x, &RldpHttpProxy::set_global_config, arg.str()); });
|
||||||
p.add_checked_option('L', "local", "http hostname that will be proxied to http server at localhost:80",
|
p.add_checked_option('L', "local",
|
||||||
|
"<hosthame>:<ports>, hostname that will be proxied to localhost\n"
|
||||||
|
"<ports> is a comma-separated list of ports (may be omitted, default: 80, 443)\n",
|
||||||
[&](td::Slice arg) -> td::Status {
|
[&](td::Slice arg) -> td::Status {
|
||||||
td::IPAddress addr;
|
return add_local_host(arg.str(), "127.0.0.1");
|
||||||
TRY_STATUS(addr.init_ipv4_port("127.0.0.1", 80));
|
|
||||||
td::actor::send_closure(x, &RldpHttpProxy::set_local_host, arg.str(), addr);
|
|
||||||
return td::Status::OK();
|
|
||||||
});
|
});
|
||||||
p.add_option('D', "db", "db root",
|
p.add_option('D', "db", "db root",
|
||||||
[&](td::Slice arg) { td::actor::send_closure(x, &RldpHttpProxy::set_db_root, arg.str()); });
|
[&](td::Slice arg) { td::actor::send_closure(x, &RldpHttpProxy::set_db_root, arg.str()); });
|
||||||
p.add_checked_option(
|
p.add_checked_option(
|
||||||
'R', "remote",
|
'R', "remote",
|
||||||
"<hostname>@<ip>:<port>, indicates a http hostname that will be proxied to remote http server at <ip>:<port>",
|
"<hostname>:<ports>@<ip>:<port>, indicates a hostname that will be proxied to remote server at <ip>:<port>\n"
|
||||||
|
"<ports> is a comma-separated list of ports (may be omitted, default: 80,433)\n"
|
||||||
|
"<port> is a remote port (may be omitted, default: same as host's port)",
|
||||||
[&](td::Slice arg) -> td::Status {
|
[&](td::Slice arg) -> td::Status {
|
||||||
auto ch = arg.find('@');
|
auto ch = arg.find('@');
|
||||||
if (ch == td::Slice::npos) {
|
if (ch == td::Slice::npos) {
|
||||||
return td::Status::Error("bad format for --remote");
|
return td::Status::Error("bad format for --remote");
|
||||||
}
|
}
|
||||||
td::IPAddress addr;
|
return add_local_host(arg.substr(0, ch).str(), arg.substr(ch + 1).str());
|
||||||
TRY_STATUS(addr.init_host_port(arg.substr(ch + 1).str()));
|
|
||||||
td::actor::send_closure(x, &RldpHttpProxy::set_local_host, arg.substr(0, ch).str(), addr);
|
|
||||||
return td::Status::OK();
|
|
||||||
});
|
});
|
||||||
p.add_option('d', "daemonize", "set SIGHUP", [&]() {
|
p.add_option('d', "daemonize", "set SIGHUP", [&]() {
|
||||||
td::set_signal_handler(td::SignalType::HangUp, [](int sig) {
|
td::set_signal_handler(td::SignalType::HangUp, [](int sig) {
|
||||||
|
|
|
@ -125,6 +125,10 @@ void RldpTransferReceiverImpl::receive_part(fec::FecType fec_type, td::uint32 pa
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!decoder_) {
|
if (!decoder_) {
|
||||||
|
if (offset_ + fec_type.size() > total_size_) {
|
||||||
|
VLOG(RLDP_NOTICE) << "failed to create decoder: data size in fec type is too big";
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto D = fec_type.create_decoder();
|
auto D = fec_type.create_decoder();
|
||||||
if (D.is_error()) {
|
if (D.is_error()) {
|
||||||
VLOG(RLDP_WARNING) << "failed to create decoder: " << D.move_as_error();
|
VLOG(RLDP_WARNING) << "failed to create decoder: " << D.move_as_error();
|
||||||
|
|
|
@ -60,12 +60,12 @@ StringBuilder &operator<<(StringBuilder &string_builder, const Timer &timer) {
|
||||||
return string_builder << format::as_time(timer.elapsed());
|
return string_builder << format::as_time(timer.elapsed());
|
||||||
}
|
}
|
||||||
|
|
||||||
PerfWarningTimer::PerfWarningTimer(string name, double max_duration)
|
PerfWarningTimer::PerfWarningTimer(string name, double max_duration, std::function<void(double)>&& callback)
|
||||||
: name_(std::move(name)), start_at_(Time::now()), max_duration_(max_duration) {
|
: name_(std::move(name)), start_at_(Time::now()), max_duration_(max_duration), callback_(std::move(callback)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
PerfWarningTimer::PerfWarningTimer(PerfWarningTimer &&other)
|
PerfWarningTimer::PerfWarningTimer(PerfWarningTimer &&other)
|
||||||
: name_(std::move(other.name_)), start_at_(other.start_at_), max_duration_(other.max_duration_) {
|
: name_(std::move(other.name_)), start_at_(other.start_at_), max_duration_(other.max_duration_), callback_(std::move(other.callback_)) {
|
||||||
other.start_at_ = 0;
|
other.start_at_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,8 +78,9 @@ void PerfWarningTimer::reset() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
double duration = Time::now() - start_at_;
|
double duration = Time::now() - start_at_;
|
||||||
LOG_IF(WARNING, duration > max_duration_)
|
//LOG_IF(WARNING, duration > max_duration_)
|
||||||
<< "SLOW: " << tag("name", name_) << tag("duration", format::as_time(duration));
|
//<< "SLOW: " << tag("name", name_) << tag("duration", format::as_time(duration));
|
||||||
|
callback_(duration);
|
||||||
start_at_ = 0;
|
start_at_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
|
|
||||||
#include "td/utils/StringBuilder.h"
|
#include "td/utils/StringBuilder.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
|
|
||||||
class Timer {
|
class Timer {
|
||||||
|
@ -44,7 +46,7 @@ class Timer {
|
||||||
|
|
||||||
class PerfWarningTimer {
|
class PerfWarningTimer {
|
||||||
public:
|
public:
|
||||||
explicit PerfWarningTimer(string name, double max_duration = 0.1);
|
explicit PerfWarningTimer(string name, double max_duration = 0.1, std::function<void(double)>&& callback = [] (double) {});
|
||||||
PerfWarningTimer(const PerfWarningTimer &) = delete;
|
PerfWarningTimer(const PerfWarningTimer &) = delete;
|
||||||
PerfWarningTimer &operator=(const PerfWarningTimer &) = delete;
|
PerfWarningTimer &operator=(const PerfWarningTimer &) = delete;
|
||||||
PerfWarningTimer(PerfWarningTimer &&other);
|
PerfWarningTimer(PerfWarningTimer &&other);
|
||||||
|
@ -56,6 +58,7 @@ class PerfWarningTimer {
|
||||||
string name_;
|
string name_;
|
||||||
double start_at_{0};
|
double start_at_{0};
|
||||||
double max_duration_{0};
|
double max_duration_{0};
|
||||||
|
std::function<void(double)> callback_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace td
|
} // namespace td
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
#define TD_EVENTFD_BSD 1
|
#define TD_EVENTFD_BSD 1
|
||||||
#elif TD_EMSCRIPTEN
|
#elif TD_EMSCRIPTEN
|
||||||
#define TD_POLL_POLL 1
|
#define TD_POLL_POLL 1
|
||||||
#define TD_EVENTFD_UNSUPPORTED 1
|
// #define TD_EVENTFD_UNSUPPORTED 1
|
||||||
#elif TD_DARWIN
|
#elif TD_DARWIN
|
||||||
#define TD_POLL_KQUEUE 1
|
#define TD_POLL_KQUEUE 1
|
||||||
#define TD_EVENTFD_BSD 1
|
#define TD_EVENTFD_BSD 1
|
||||||
|
@ -51,7 +51,11 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if TD_EMSCRIPTEN
|
#if TD_EMSCRIPTEN
|
||||||
#define TD_THREAD_UNSUPPORTED 1
|
// #define TD_THREAD_UNSUPPORTED 1
|
||||||
|
#define TD_POLL_EPOLL 1
|
||||||
|
#define TD_EVENTFD_UNSUPPORTED 0
|
||||||
|
#define TD_THREAD_PTHREAD 1
|
||||||
|
#define TD_EVENTFD_LINUX 1
|
||||||
#elif TD_TIZEN || TD_LINUX || TD_DARWIN
|
#elif TD_TIZEN || TD_LINUX || TD_DARWIN
|
||||||
#define TD_THREAD_PTHREAD 1
|
#define TD_THREAD_PTHREAD 1
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -52,6 +52,8 @@ liteServer.partialBlockProof complete:Bool from:tonNode.blockIdExt to:tonNode.bl
|
||||||
liteServer.configInfo mode:# id:tonNode.blockIdExt state_proof:bytes config_proof:bytes = liteServer.ConfigInfo;
|
liteServer.configInfo mode:# id:tonNode.blockIdExt state_proof:bytes config_proof:bytes = liteServer.ConfigInfo;
|
||||||
liteServer.validatorStats mode:# id:tonNode.blockIdExt count:int complete:Bool state_proof:bytes data_proof:bytes = liteServer.ValidatorStats;
|
liteServer.validatorStats mode:# id:tonNode.blockIdExt count:int complete:Bool state_proof:bytes data_proof:bytes = liteServer.ValidatorStats;
|
||||||
liteServer.libraryResult result:(vector liteServer.libraryEntry) = liteServer.LibraryResult;
|
liteServer.libraryResult result:(vector liteServer.libraryEntry) = liteServer.LibraryResult;
|
||||||
|
liteServer.shardBlockLink id:tonNode.blockIdExt proof:bytes = liteServer.ShardBlockLink;
|
||||||
|
liteServer.shardBlockProof masterchain_id:tonNode.blockIdExt links:(vector liteServer.shardBlockLink) = liteServer.ShardBlockProof;
|
||||||
|
|
||||||
liteServer.debug.verbosity value:int = liteServer.debug.Verbosity;
|
liteServer.debug.verbosity value:int = liteServer.debug.Verbosity;
|
||||||
|
|
||||||
|
@ -78,6 +80,7 @@ liteServer.getConfigAll mode:# id:tonNode.blockIdExt = liteServer.ConfigInfo;
|
||||||
liteServer.getConfigParams mode:# id:tonNode.blockIdExt param_list:(vector int) = liteServer.ConfigInfo;
|
liteServer.getConfigParams mode:# id:tonNode.blockIdExt param_list:(vector int) = liteServer.ConfigInfo;
|
||||||
liteServer.getValidatorStats#091a58bc mode:# id:tonNode.blockIdExt limit:int start_after:mode.0?int256 modified_after:mode.2?int = liteServer.ValidatorStats;
|
liteServer.getValidatorStats#091a58bc mode:# id:tonNode.blockIdExt limit:int start_after:mode.0?int256 modified_after:mode.2?int = liteServer.ValidatorStats;
|
||||||
liteServer.getLibraries library_list:(vector int256) = liteServer.LibraryResult;
|
liteServer.getLibraries library_list:(vector int256) = liteServer.LibraryResult;
|
||||||
|
liteServer.getShardBlockProof id:tonNode.blockIdExt = liteServer.ShardBlockProof;
|
||||||
|
|
||||||
liteServer.queryPrefix = Object;
|
liteServer.queryPrefix = Object;
|
||||||
liteServer.query data:bytes = Object;
|
liteServer.query data:bytes = Object;
|
||||||
|
|
Binary file not shown.
|
@ -651,6 +651,10 @@ engine.validator.overlayStatsNode adnl_id:int256 ip_addr:string bdcst_errors:int
|
||||||
engine.validator.overlayStats overlay_id:int256 overlay_id_full:PublicKey adnl_id:int256 scope:string nodes:(vector engine.validator.overlayStatsNode) stats:(vector engine.validator.oneStat) = engine.validator.OverlayStats;
|
engine.validator.overlayStats overlay_id:int256 overlay_id_full:PublicKey adnl_id:int256 scope:string nodes:(vector engine.validator.overlayStatsNode) stats:(vector engine.validator.oneStat) = engine.validator.OverlayStats;
|
||||||
engine.validator.overlaysStats overlays:(vector engine.validator.overlayStats) = engine.validator.OverlaysStats;
|
engine.validator.overlaysStats overlays:(vector engine.validator.overlayStats) = engine.validator.OverlaysStats;
|
||||||
|
|
||||||
|
engine.validator.onePerfTimerStat time:int min:double avg:double max:double = engine.validator.OnePerfTimerStat;
|
||||||
|
engine.validator.perfTimerStatsByName name:string stats:(vector engine.validator.OnePerfTimerStat) = engine.validator.PerfTimerStatsByName;
|
||||||
|
engine.validator.perfTimerStats stats:(vector engine.validator.PerfTimerStatsByName) = engine.validator.PerfTimerStats;
|
||||||
|
|
||||||
engine.validator.validatorSessionInfo current_block:tonNode.blockId self:int256 current_round:int next_producers:(vector int256) = engine.validator.ValidatorSessionInfo;
|
engine.validator.validatorSessionInfo current_block:tonNode.blockId self:int256 current_round:int next_producers:(vector int256) = engine.validator.ValidatorSessionInfo;
|
||||||
engine.validator.validatorSessionsInfo sessions:(vector engine.validator.validatorSessionInfo) = engine.validator.ValidatorSessionsInfo;
|
engine.validator.validatorSessionsInfo sessions:(vector engine.validator.validatorSessionInfo) = engine.validator.ValidatorSessionsInfo;
|
||||||
|
|
||||||
|
@ -705,6 +709,8 @@ engine.validator.importCertificate overlay_id:int256 local_id:adnl.id.short sign
|
||||||
engine.validator.signShardOverlayCertificate workchain:int shard:long signed_key:engine.validator.KeyHash expire_at:int max_size:int = overlay.Certificate;
|
engine.validator.signShardOverlayCertificate workchain:int shard:long signed_key:engine.validator.KeyHash expire_at:int max_size:int = overlay.Certificate;
|
||||||
engine.validator.importShardOverlayCertificate workchain:int shard:long signed_key:engine.validator.KeyHash cert:overlay.Certificate = engine.validator.Success;
|
engine.validator.importShardOverlayCertificate workchain:int shard:long signed_key:engine.validator.KeyHash cert:overlay.Certificate = engine.validator.Success;
|
||||||
|
|
||||||
|
engine.validator.getPerfTimerStats name:string = engine.validator.PerfTimerStats;
|
||||||
|
|
||||||
engine.validator.getValidatorSessionsInfo = engine.validator.ValidatorSessionsInfo;
|
engine.validator.getValidatorSessionsInfo = engine.validator.ValidatorSessionsInfo;
|
||||||
|
|
||||||
engine.validator.addCollator adnl_id:int256 shard:tonNode.shardId = engine.validator.Success;
|
engine.validator.addCollator adnl_id:int256 shard:tonNode.shardId = engine.validator.Success;
|
||||||
|
@ -735,7 +741,7 @@ storage.queryPrefix id:int256 = Object;
|
||||||
|
|
||||||
http.header name:string value:string = http.Header;
|
http.header name:string value:string = http.Header;
|
||||||
http.payloadPart data:bytes trailer:(vector http.header) last:Bool = http.PayloadPart;
|
http.payloadPart data:bytes trailer:(vector http.header) last:Bool = http.PayloadPart;
|
||||||
http.response http_version:string status_code:int reason:string headers:(vector http.header) = http.Response;
|
http.response http_version:string status_code:int reason:string headers:(vector http.header) no_payload:Bool = http.Response;
|
||||||
|
|
||||||
---functions---
|
---functions---
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -217,6 +217,12 @@ blocks.transactions id:ton.blockIdExt req_count:int32 incomplete:Bool transactio
|
||||||
blocks.header id:ton.blockIdExt global_id:int32 version:int32 flags:# after_merge:Bool after_split:Bool before_split:Bool want_merge:Bool want_split:Bool validator_list_hash_short:int32 catchain_seqno:int32 min_ref_mc_seqno:int32 is_key_block:Bool prev_key_block_seqno:int32 start_lt:int64 end_lt:int64 gen_utime:int53 vert_seqno:# prev_blocks:vector<ton.blockIdExt> = blocks.Header;
|
blocks.header id:ton.blockIdExt global_id:int32 version:int32 flags:# after_merge:Bool after_split:Bool before_split:Bool want_merge:Bool want_split:Bool validator_list_hash_short:int32 catchain_seqno:int32 min_ref_mc_seqno:int32 is_key_block:Bool prev_key_block_seqno:int32 start_lt:int64 end_lt:int64 gen_utime:int53 vert_seqno:# prev_blocks:vector<ton.blockIdExt> = blocks.Header;
|
||||||
//blocks.shortData header:blocks.Header transactions:blocks.Header = blocks.BlockData;
|
//blocks.shortData header:blocks.Header transactions:blocks.Header = blocks.BlockData;
|
||||||
|
|
||||||
|
blocks.signature node_id_short:int256 signature:bytes = blocks.Signature;
|
||||||
|
blocks.blockSignatures id:ton.blockIdExt signatures:(vector blocks.signature) = blocks.BlockSignatures;
|
||||||
|
blocks.shardBlockLink id:ton.blockIdExt proof:bytes = blocks.ShardBlockLink;
|
||||||
|
blocks.blockLinkBack to_key_block:Bool from:ton.blockIdExt to:ton.blockIdExt dest_proof:bytes proof:bytes state_proof:bytes = blocks.BlockLinkBack;
|
||||||
|
blocks.shardBlockProof from:ton.blockIdExt mc_id:ton.blockIdExt links:(vector blocks.shardBlockLink) mc_proof:(vector blocks.blockLinkBack) = blocks.ShardBlockProof;
|
||||||
|
|
||||||
configInfo config:tvm.cell = ConfigInfo;
|
configInfo config:tvm.cell = ConfigInfo;
|
||||||
|
|
||||||
---functions---
|
---functions---
|
||||||
|
@ -251,6 +257,7 @@ getBip39Hints prefix:string = Bip39Hints;
|
||||||
//raw.init initial_account_state:raw.initialAccountState = Ok;
|
//raw.init initial_account_state:raw.initialAccountState = Ok;
|
||||||
raw.getAccountState account_address:accountAddress = raw.FullAccountState;
|
raw.getAccountState account_address:accountAddress = raw.FullAccountState;
|
||||||
raw.getTransactions private_key:InputKey account_address:accountAddress from_transaction_id:internal.transactionId = raw.Transactions;
|
raw.getTransactions private_key:InputKey account_address:accountAddress from_transaction_id:internal.transactionId = raw.Transactions;
|
||||||
|
raw.getTransactionsV2 private_key:InputKey account_address:accountAddress from_transaction_id:internal.transactionId count:# try_decode_messages:Bool = raw.Transactions;
|
||||||
raw.sendMessage body:bytes = Ok;
|
raw.sendMessage body:bytes = Ok;
|
||||||
raw.sendMessageReturnHash body:bytes = raw.ExtMessageInfo;
|
raw.sendMessageReturnHash body:bytes = raw.ExtMessageInfo;
|
||||||
raw.createAndSendMessage destination:accountAddress initial_account_state:bytes data:bytes = Ok;
|
raw.createAndSendMessage destination:accountAddress initial_account_state:bytes data:bytes = Ok;
|
||||||
|
@ -306,6 +313,8 @@ blocks.getShards id:ton.blockIdExt = blocks.Shards;
|
||||||
blocks.lookupBlock mode:int32 id:ton.blockId lt:int64 utime:int32 = ton.BlockIdExt;
|
blocks.lookupBlock mode:int32 id:ton.blockId lt:int64 utime:int32 = ton.BlockIdExt;
|
||||||
blocks.getTransactions id:ton.blockIdExt mode:# count:# after:blocks.accountTransactionId = blocks.Transactions;
|
blocks.getTransactions id:ton.blockIdExt mode:# count:# after:blocks.accountTransactionId = blocks.Transactions;
|
||||||
blocks.getBlockHeader id:ton.blockIdExt = blocks.Header;
|
blocks.getBlockHeader id:ton.blockIdExt = blocks.Header;
|
||||||
|
blocks.getMasterchainBlockSignatures seqno:int32 = blocks.BlockSignatures;
|
||||||
|
blocks.getShardBlockProof id:ton.blockIdExt mode:# from:mode.0?ton.blockIdExt = blocks.ShardBlockProof;
|
||||||
|
|
||||||
onLiteServerQueryResult id:int64 bytes:bytes = Ok;
|
onLiteServerQueryResult id:int64 bytes:bytes = Ok;
|
||||||
onLiteServerQueryError id:int64 error:error = Ok;
|
onLiteServerQueryError id:int64 error:error = Ok;
|
||||||
|
|
Binary file not shown.
|
@ -49,6 +49,8 @@ set(TONLIB_SOURCE
|
||||||
set(TONLIB_OFFLINE_TEST_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/test/offline.cpp PARENT_SCOPE)
|
set(TONLIB_OFFLINE_TEST_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/test/offline.cpp PARENT_SCOPE)
|
||||||
set(TONLIB_ONLINE_TEST_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/test/online.cpp PARENT_SCOPE)
|
set(TONLIB_ONLINE_TEST_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/test/online.cpp PARENT_SCOPE)
|
||||||
|
|
||||||
|
set(USE_EMSCRIPTEN ${USE_EMSCRIPTEN} PARENT_SCOPE)
|
||||||
|
|
||||||
add_library(tonlib STATIC ${TONLIB_SOURCE})
|
add_library(tonlib STATIC ${TONLIB_SOURCE})
|
||||||
|
|
||||||
target_include_directories(tonlib PUBLIC
|
target_include_directories(tonlib PUBLIC
|
||||||
|
@ -87,8 +89,12 @@ set(TONLIB_JSON_HEADERS tonlib/tonlib_client_json.h)
|
||||||
set(TONLIB_JSON_SOURCE tonlib/tonlib_client_json.cpp)
|
set(TONLIB_JSON_SOURCE tonlib/tonlib_client_json.cpp)
|
||||||
|
|
||||||
include(GenerateExportHeader)
|
include(GenerateExportHeader)
|
||||||
|
if (NOT USE_EMSCRIPTEN)
|
||||||
|
add_library(tonlibjson SHARED ${TONLIB_JSON_SOURCE} ${TONLIB_JSON_HEADERS})
|
||||||
|
else()
|
||||||
|
add_library(tonlibjson STATIC ${TONLIB_JSON_SOURCE} ${TONLIB_JSON_HEADERS})
|
||||||
|
endif()
|
||||||
|
|
||||||
add_library(tonlibjson SHARED ${TONLIB_JSON_SOURCE} ${TONLIB_JSON_HEADERS})
|
|
||||||
target_link_libraries(tonlibjson PRIVATE tonlibjson_private)
|
target_link_libraries(tonlibjson PRIVATE tonlibjson_private)
|
||||||
generate_export_header(tonlibjson EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/tonlib/tonlibjson_export.h)
|
generate_export_header(tonlibjson EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/tonlib/tonlibjson_export.h)
|
||||||
target_include_directories(tonlibjson PUBLIC
|
target_include_directories(tonlibjson PUBLIC
|
||||||
|
@ -149,11 +155,13 @@ endif()
|
||||||
|
|
||||||
install(FILES ${TONLIB_JSON_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/tonlib/tonlibjson_export.h DESTINATION include/tonlib/)
|
install(FILES ${TONLIB_JSON_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/tonlib/tonlibjson_export.h DESTINATION include/tonlib/)
|
||||||
|
|
||||||
install(EXPORT Tonlib
|
if (NOT USE_EMSCRIPTEN)
|
||||||
|
install(EXPORT Tonlib
|
||||||
FILE TonlibTargets.cmake
|
FILE TonlibTargets.cmake
|
||||||
NAMESPACE Tonlib::
|
NAMESPACE Tonlib::
|
||||||
DESTINATION lib/cmake/Tonlib
|
DESTINATION lib/cmake/Tonlib
|
||||||
)
|
)
|
||||||
|
endif()
|
||||||
include(CMakePackageConfigHelpers)
|
include(CMakePackageConfigHelpers)
|
||||||
write_basic_package_version_file("TonlibConfigVersion.cmake"
|
write_basic_package_version_file("TonlibConfigVersion.cmake"
|
||||||
VERSION ${TON_VERSION}
|
VERSION ${TON_VERSION}
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include "block/check-proof.h"
|
#include "block/check-proof.h"
|
||||||
#include "ton/lite-tl.hpp"
|
#include "ton/lite-tl.hpp"
|
||||||
#include "ton/ton-shard.h"
|
#include "ton/ton-shard.h"
|
||||||
|
#include "lite-client/lite-client-common.h"
|
||||||
|
|
||||||
#include "vm/boc.h"
|
#include "vm/boc.h"
|
||||||
#include "vm/cellops.h"
|
#include "vm/cellops.h"
|
||||||
|
@ -960,11 +961,12 @@ td::Result<td::int64> to_balance(td::Ref<vm::CellSlice> balance_ref) {
|
||||||
|
|
||||||
class GetTransactionHistory : public td::actor::Actor {
|
class GetTransactionHistory : public td::actor::Actor {
|
||||||
public:
|
public:
|
||||||
GetTransactionHistory(ExtClientRef ext_client_ref, block::StdAddress address, ton::LogicalTime lt, ton::Bits256 hash,
|
GetTransactionHistory(ExtClientRef ext_client_ref, block::StdAddress address, ton::LogicalTime lt, ton::Bits256 hash, td::int32 count,
|
||||||
td::actor::ActorShared<> parent, td::Promise<block::TransactionList::Info> promise)
|
td::actor::ActorShared<> parent, td::Promise<block::TransactionList::Info> promise)
|
||||||
: address_(std::move(address))
|
: address_(std::move(address))
|
||||||
, lt_(std::move(lt))
|
, lt_(std::move(lt))
|
||||||
, hash_(std::move(hash))
|
, hash_(std::move(hash))
|
||||||
|
, count_(count)
|
||||||
, parent_(std::move(parent))
|
, parent_(std::move(parent))
|
||||||
, promise_(std::move(promise)) {
|
, promise_(std::move(promise)) {
|
||||||
client_.set_client(ext_client_ref);
|
client_.set_client(ext_client_ref);
|
||||||
|
@ -975,7 +977,7 @@ class GetTransactionHistory : public td::actor::Actor {
|
||||||
ton::LogicalTime lt_;
|
ton::LogicalTime lt_;
|
||||||
ton::Bits256 hash_;
|
ton::Bits256 hash_;
|
||||||
ExtClient client_;
|
ExtClient client_;
|
||||||
td::int32 count_{10};
|
td::int32 count_;
|
||||||
td::actor::ActorShared<> parent_;
|
td::actor::ActorShared<> parent_;
|
||||||
td::Promise<block::TransactionList::Info> promise_;
|
td::Promise<block::TransactionList::Info> promise_;
|
||||||
|
|
||||||
|
@ -1316,6 +1318,321 @@ class GetRawAccountState : public td::actor::Actor {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GetMasterchainBlockSignatures : public td::actor::Actor {
|
||||||
|
public:
|
||||||
|
GetMasterchainBlockSignatures(ExtClientRef ext_client_ref, ton::BlockSeqno seqno, td::actor::ActorShared<> parent,
|
||||||
|
td::Promise<tonlib_api_ptr<tonlib_api::blocks_blockSignatures>>&& promise)
|
||||||
|
: block_id_short_(ton::masterchainId, ton::shardIdAll, seqno)
|
||||||
|
, parent_(std::move(parent))
|
||||||
|
, promise_(std::move(promise)) {
|
||||||
|
client_.set_client(ext_client_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_up() override {
|
||||||
|
if (block_id_short_.seqno == 0) {
|
||||||
|
abort(td::Status::Error("can't get signatures of block #0"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
client_.with_last_block([SelfId = actor_id(this)](td::Result<LastBlockState> R) {
|
||||||
|
if (R.is_error()) {
|
||||||
|
td::actor::send_closure(SelfId, &GetMasterchainBlockSignatures::abort, R.move_as_error());
|
||||||
|
} else {
|
||||||
|
td::actor::send_closure(SelfId, &GetMasterchainBlockSignatures::got_last_block, R.ok().last_block_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void got_last_block(ton::BlockIdExt id) {
|
||||||
|
last_block_ = id;
|
||||||
|
prev_block_id_short_ = block_id_short_;
|
||||||
|
prev_block_id_short_.seqno--;
|
||||||
|
client_.send_query(
|
||||||
|
ton::lite_api::liteServer_lookupBlock(1, ton::create_tl_lite_block_id_simple(prev_block_id_short_), 0, 0),
|
||||||
|
[SelfId = actor_id(this)](td::Result<lite_api_ptr<ton::lite_api::liteServer_blockHeader>> R) {
|
||||||
|
if (R.is_error()) {
|
||||||
|
td::actor::send_closure(SelfId, &GetMasterchainBlockSignatures::abort, R.move_as_error());
|
||||||
|
} else {
|
||||||
|
td::actor::send_closure(SelfId, &GetMasterchainBlockSignatures::got_prev_block_id,
|
||||||
|
ton::create_block_id(R.ok()->id_));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void got_prev_block_id(ton::BlockIdExt id) {
|
||||||
|
prev_block_id_ = id;
|
||||||
|
if (prev_block_id_.id != prev_block_id_short_) {
|
||||||
|
abort(td::Status::Error("got incorrect block header from liteserver"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
client_.send_query(
|
||||||
|
ton::lite_api::liteServer_getBlockProof(0x1001, ton::create_tl_lite_block_id(last_block_),
|
||||||
|
ton::create_tl_lite_block_id(prev_block_id_)),
|
||||||
|
[SelfId = actor_id(this)](td::Result<lite_api_ptr<ton::lite_api::liteServer_partialBlockProof>> R) {
|
||||||
|
if (R.is_error()) {
|
||||||
|
td::actor::send_closure(SelfId, &GetMasterchainBlockSignatures::abort, R.move_as_error());
|
||||||
|
} else {
|
||||||
|
td::actor::send_closure(SelfId, &GetMasterchainBlockSignatures::got_prev_proof, R.move_as_ok());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void got_prev_proof(lite_api_ptr<ton::lite_api::liteServer_partialBlockProof> proof) {
|
||||||
|
auto R = liteclient::deserialize_proof_chain(std::move(proof));
|
||||||
|
if (R.is_error()) {
|
||||||
|
abort(R.move_as_error());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto chain = R.move_as_ok();
|
||||||
|
if (chain->from != last_block_ || chain->to != prev_block_id_ || !chain->complete) {
|
||||||
|
abort(td::Status::Error("got invalid proof chain"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto S = chain->validate();
|
||||||
|
if (S.is_error()) {
|
||||||
|
abort(std::move(S));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
client_.send_query(
|
||||||
|
ton::lite_api::liteServer_lookupBlock(1, ton::create_tl_lite_block_id_simple(block_id_short_), 0, 0),
|
||||||
|
[SelfId = actor_id(this)](td::Result<lite_api_ptr<ton::lite_api::liteServer_blockHeader>> R) {
|
||||||
|
if (R.is_error()) {
|
||||||
|
td::actor::send_closure(SelfId, &GetMasterchainBlockSignatures::abort, R.move_as_error());
|
||||||
|
} else {
|
||||||
|
td::actor::send_closure(SelfId, &GetMasterchainBlockSignatures::got_block_id,
|
||||||
|
ton::create_block_id(R.ok()->id_));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void got_block_id(ton::BlockIdExt id) {
|
||||||
|
block_id_ = id;
|
||||||
|
client_.send_query(
|
||||||
|
ton::lite_api::liteServer_getBlockProof(0x1001, ton::create_tl_lite_block_id(prev_block_id_),
|
||||||
|
ton::create_tl_lite_block_id(block_id_)),
|
||||||
|
[SelfId = actor_id(this)](td::Result<lite_api_ptr<ton::lite_api::liteServer_partialBlockProof>> R) {
|
||||||
|
if (R.is_error()) {
|
||||||
|
td::actor::send_closure(SelfId, &GetMasterchainBlockSignatures::abort, R.move_as_error());
|
||||||
|
} else {
|
||||||
|
td::actor::send_closure(SelfId, &GetMasterchainBlockSignatures::got_proof, R.move_as_ok());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void got_proof(lite_api_ptr<ton::lite_api::liteServer_partialBlockProof> proof) {
|
||||||
|
auto R = liteclient::deserialize_proof_chain(std::move(proof));
|
||||||
|
if (R.is_error()) {
|
||||||
|
abort(R.move_as_error());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto chain = R.move_as_ok();
|
||||||
|
if (chain->from != prev_block_id_ || chain->to != block_id_ || !chain->complete || chain->links.empty() ||
|
||||||
|
chain->last_link().signatures.empty()) {
|
||||||
|
abort(td::Status::Error("got invalid proof chain"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto S = chain->validate();
|
||||||
|
if (S.is_error()) {
|
||||||
|
abort(std::move(S));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::vector<tonlib_api_ptr<tonlib_api::blocks_signature>> signatures;
|
||||||
|
for (const auto& s : chain->last_link().signatures) {
|
||||||
|
signatures.push_back(ton::create_tl_object<tonlib_api::blocks_signature>(s.node, s.signature.as_slice().str()));
|
||||||
|
}
|
||||||
|
promise_.set_result(
|
||||||
|
ton::create_tl_object<tonlib_api::blocks_blockSignatures>(to_tonlib_api(block_id_), std::move(signatures)));
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void abort(td::Status error) {
|
||||||
|
promise_.set_error(std::move(error));
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ton::BlockId block_id_short_;
|
||||||
|
td::actor::ActorShared<> parent_;
|
||||||
|
td::Promise<tonlib_api_ptr<tonlib_api::blocks_blockSignatures>> promise_;
|
||||||
|
ExtClient client_;
|
||||||
|
ton::BlockIdExt block_id_;
|
||||||
|
ton::BlockId prev_block_id_short_;
|
||||||
|
ton::BlockIdExt prev_block_id_;
|
||||||
|
ton::BlockIdExt last_block_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GetShardBlockProof : public td::actor::Actor {
|
||||||
|
public:
|
||||||
|
GetShardBlockProof(ExtClientRef ext_client_ref, ton::BlockIdExt id, ton::BlockIdExt from,
|
||||||
|
td::actor::ActorShared<> parent,
|
||||||
|
td::Promise<tonlib_api_ptr<tonlib_api::blocks_shardBlockProof>>&& promise)
|
||||||
|
: id_(id), from_(from), parent_(std::move(parent)), promise_(std::move(promise)) {
|
||||||
|
client_.set_client(ext_client_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_up() override {
|
||||||
|
if (from_.is_masterchain_ext()) {
|
||||||
|
got_from_block(from_);
|
||||||
|
} else {
|
||||||
|
client_.with_last_block([SelfId = actor_id(this)](td::Result<LastBlockState> R) {
|
||||||
|
if (R.is_error()) {
|
||||||
|
td::actor::send_closure(SelfId, &GetShardBlockProof::abort, R.move_as_error());
|
||||||
|
} else {
|
||||||
|
td::actor::send_closure(SelfId, &GetShardBlockProof::got_from_block, R.move_as_ok().last_block_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void got_from_block(ton::BlockIdExt from) {
|
||||||
|
from_ = from;
|
||||||
|
CHECK(from_.is_masterchain_ext());
|
||||||
|
client_.send_query(
|
||||||
|
ton::lite_api::liteServer_getShardBlockProof(ton::create_tl_lite_block_id(id_)),
|
||||||
|
[SelfId = actor_id(this)](td::Result<lite_api_ptr<ton::lite_api::liteServer_shardBlockProof>> R) {
|
||||||
|
if (R.is_error()) {
|
||||||
|
td::actor::send_closure(SelfId, &GetShardBlockProof::abort, R.move_as_error());
|
||||||
|
} else {
|
||||||
|
td::actor::send_closure(SelfId, &GetShardBlockProof::got_shard_block_proof, R.move_as_ok());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void got_shard_block_proof(lite_api_ptr<ton::lite_api::liteServer_shardBlockProof> result) {
|
||||||
|
mc_id_ = create_block_id(std::move(result->masterchain_id_));
|
||||||
|
if (!mc_id_.is_masterchain_ext()) {
|
||||||
|
abort(td::Status::Error("got invalid masterchain block id"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result->links_.size() > 8) {
|
||||||
|
abort(td::Status::Error("chain is too long"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ton::BlockIdExt cur_id = mc_id_;
|
||||||
|
try {
|
||||||
|
for (auto& link : result->links_) {
|
||||||
|
ton::BlockIdExt prev_id = create_block_id(link->id_);
|
||||||
|
td::BufferSlice proof = std::move(link->proof_);
|
||||||
|
auto R = vm::std_boc_deserialize(proof);
|
||||||
|
if (R.is_error()) {
|
||||||
|
abort(TonlibError::InvalidBagOfCells("proof"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto block_root = vm::MerkleProof::virtualize(R.move_as_ok(), 1);
|
||||||
|
if (cur_id.root_hash != block_root->get_hash().bits()) {
|
||||||
|
abort(td::Status::Error("invalid block hash in proof"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cur_id.is_masterchain()) {
|
||||||
|
block::gen::Block::Record blk;
|
||||||
|
block::gen::BlockExtra::Record extra;
|
||||||
|
block::gen::McBlockExtra::Record mc_extra;
|
||||||
|
if (!tlb::unpack_cell(block_root, blk) || !tlb::unpack_cell(blk.extra, extra) || !extra.custom->have_refs() ||
|
||||||
|
!tlb::unpack_cell(extra.custom->prefetch_ref(), mc_extra)) {
|
||||||
|
abort(td::Status::Error("cannot unpack block header"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
block::ShardConfig shards(mc_extra.shard_hashes->prefetch_ref());
|
||||||
|
td::Ref<block::McShardHash> shard_hash = shards.get_shard_hash(prev_id.shard_full(), true);
|
||||||
|
if (shard_hash.is_null() || shard_hash->top_block_id() != prev_id) {
|
||||||
|
abort(td::Status::Error("invalid proof chain: prev block is not in mc shard list"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::vector<ton::BlockIdExt> prev;
|
||||||
|
ton::BlockIdExt mc_blkid;
|
||||||
|
bool after_split;
|
||||||
|
td::Status S = block::unpack_block_prev_blk_try(block_root, cur_id, prev, mc_blkid, after_split);
|
||||||
|
if (S.is_error()) {
|
||||||
|
abort(std::move(S));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CHECK(prev.size() == 1 || prev.size() == 2);
|
||||||
|
bool found = prev_id == prev[0] || (prev.size() == 2 && prev_id == prev[1]);
|
||||||
|
if (!found) {
|
||||||
|
abort(td::Status::Error("invalid proof chain: prev block is not in prev blocks list"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
links_.emplace_back(prev_id, std::move(proof));
|
||||||
|
cur_id = prev_id;
|
||||||
|
}
|
||||||
|
} catch (vm::VmVirtError& err) {
|
||||||
|
abort(err.as_status());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cur_id != id_) {
|
||||||
|
abort(td::Status::Error("got invalid proof chain"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mc_id_.seqno() > from_.seqno()) {
|
||||||
|
abort(td::Status::Error("from mc block is too old"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client_.send_query(
|
||||||
|
ton::lite_api::liteServer_getBlockProof(0x1001, ton::create_tl_lite_block_id(from_),
|
||||||
|
ton::create_tl_lite_block_id(mc_id_)),
|
||||||
|
[SelfId = actor_id(this)](td::Result<lite_api_ptr<ton::lite_api::liteServer_partialBlockProof>> R) {
|
||||||
|
if (R.is_error()) {
|
||||||
|
td::actor::send_closure(SelfId, &GetShardBlockProof::abort, R.move_as_error());
|
||||||
|
} else {
|
||||||
|
td::actor::send_closure(SelfId, &GetShardBlockProof::got_mc_proof, R.move_as_ok());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void got_mc_proof(lite_api_ptr<ton::lite_api::liteServer_partialBlockProof> result) {
|
||||||
|
auto R = liteclient::deserialize_proof_chain(std::move(result));
|
||||||
|
if (R.is_error()) {
|
||||||
|
abort(R.move_as_error());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto chain = R.move_as_ok();
|
||||||
|
if (chain->from != from_ || chain->to != mc_id_ || !chain->complete || chain->link_count() > 1) {
|
||||||
|
abort(td::Status::Error("got invalid proof chain"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto S = chain->validate();
|
||||||
|
if (S.is_error()) {
|
||||||
|
abort(std::move(S));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ton::tl_object_ptr<tonlib_api::blocks_shardBlockLink>> links;
|
||||||
|
std::vector<ton::tl_object_ptr<tonlib_api::blocks_blockLinkBack>> mc_proof;
|
||||||
|
for (const auto& p : links_) {
|
||||||
|
links.push_back(
|
||||||
|
ton::create_tl_object<tonlib_api::blocks_shardBlockLink>(to_tonlib_api(p.first), p.second.as_slice().str()));
|
||||||
|
}
|
||||||
|
if (chain->link_count() == 1) {
|
||||||
|
auto& link = chain->last_link();
|
||||||
|
td::BufferSlice dest_proof = vm::std_boc_serialize(link.dest_proof).move_as_ok();
|
||||||
|
td::BufferSlice proof = vm::std_boc_serialize(link.proof).move_as_ok();
|
||||||
|
td::BufferSlice state_proof = vm::std_boc_serialize(link.state_proof).move_as_ok();
|
||||||
|
mc_proof.push_back(ton::create_tl_object<tonlib_api::blocks_blockLinkBack>(
|
||||||
|
link.is_key, to_tonlib_api(link.from), to_tonlib_api(link.to), dest_proof.as_slice().str(),
|
||||||
|
proof.as_slice().str(), state_proof.as_slice().str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
promise_.set_result(ton::create_tl_object<tonlib_api::blocks_shardBlockProof>(
|
||||||
|
to_tonlib_api(from_), to_tonlib_api(mc_id_), std::move(links), std::move(mc_proof)));
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void abort(td::Status error) {
|
||||||
|
promise_.set_error(std::move(error));
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ton::BlockIdExt id_, from_, mc_id_;
|
||||||
|
td::actor::ActorShared<> parent_;
|
||||||
|
td::Promise<tonlib_api_ptr<tonlib_api::blocks_shardBlockProof>> promise_;
|
||||||
|
ExtClient client_;
|
||||||
|
std::vector<std::pair<ton::BlockIdExt, td::BufferSlice>> links_;
|
||||||
|
};
|
||||||
|
|
||||||
TonlibClient::TonlibClient(td::unique_ptr<TonlibCallback> callback) : callback_(std::move(callback)) {
|
TonlibClient::TonlibClient(td::unique_ptr<TonlibCallback> callback) : callback_(std::move(callback)) {
|
||||||
}
|
}
|
||||||
TonlibClient::~TonlibClient() = default;
|
TonlibClient::~TonlibClient() = default;
|
||||||
|
@ -2170,10 +2487,14 @@ td::Result<std::string> to_std_address(td::Ref<vm::CellSlice> cs) {
|
||||||
return TRY_VM(to_std_address_or_throw(std::move(cs)));
|
return TRY_VM(to_std_address_or_throw(std::move(cs)));
|
||||||
}
|
}
|
||||||
struct ToRawTransactions {
|
struct ToRawTransactions {
|
||||||
explicit ToRawTransactions(td::optional<td::Ed25519::PrivateKey> private_key) : private_key_(std::move(private_key)) {
|
explicit ToRawTransactions(td::optional<td::Ed25519::PrivateKey> private_key, bool try_decode_messages = true)
|
||||||
|
: private_key_(std::move(private_key))
|
||||||
|
, try_decode_messages_(try_decode_messages) {
|
||||||
}
|
}
|
||||||
|
|
||||||
td::optional<td::Ed25519::PrivateKey> private_key_;
|
td::optional<td::Ed25519::PrivateKey> private_key_;
|
||||||
|
bool try_decode_messages_;
|
||||||
|
|
||||||
td::Result<tonlib_api::object_ptr<tonlib_api::raw_message>> to_raw_message_or_throw(td::Ref<vm::Cell> cell) {
|
td::Result<tonlib_api::object_ptr<tonlib_api::raw_message>> to_raw_message_or_throw(td::Ref<vm::Cell> cell) {
|
||||||
block::gen::Message::Record message;
|
block::gen::Message::Record message;
|
||||||
if (!tlb::type_unpack_cell(cell, block::gen::t_Message_Any, message)) {
|
if (!tlb::type_unpack_cell(cell, block::gen::t_Message_Any, message)) {
|
||||||
|
@ -2192,7 +2513,7 @@ struct ToRawTransactions {
|
||||||
|
|
||||||
auto get_data = [body = std::move(body), body_cell, this](td::Slice salt) mutable {
|
auto get_data = [body = std::move(body), body_cell, this](td::Slice salt) mutable {
|
||||||
tonlib_api::object_ptr<tonlib_api::msg_Data> data;
|
tonlib_api::object_ptr<tonlib_api::msg_Data> data;
|
||||||
if (body->size() >= 32 && static_cast<td::uint32>(body->prefetch_long(32)) <= 1) {
|
if (try_decode_messages_ && body->size() >= 32 && static_cast<td::uint32>(body->prefetch_long(32)) <= 1) {
|
||||||
auto type = body.write().fetch_long(32);
|
auto type = body.write().fetch_long(32);
|
||||||
td::Status status;
|
td::Status status;
|
||||||
|
|
||||||
|
@ -2203,7 +2524,6 @@ struct ToRawTransactions {
|
||||||
if (type == 0) {
|
if (type == 0) {
|
||||||
data = tonlib_api::make_object<tonlib_api::msg_dataText>(r_body_message.move_as_ok());
|
data = tonlib_api::make_object<tonlib_api::msg_dataText>(r_body_message.move_as_ok());
|
||||||
} else {
|
} else {
|
||||||
LOG(ERROR) << "TRY DECRYPT";
|
|
||||||
auto encrypted_message = r_body_message.move_as_ok();
|
auto encrypted_message = r_body_message.move_as_ok();
|
||||||
auto r_decrypted_message = [&]() -> td::Result<std::string> {
|
auto r_decrypted_message = [&]() -> td::Result<std::string> {
|
||||||
if (!private_key_) {
|
if (!private_key_) {
|
||||||
|
@ -2464,13 +2784,56 @@ td::Status TonlibClient::do_request(tonlib_api::raw_getTransactions& request,
|
||||||
|
|
||||||
auto actor_id = actor_id_++;
|
auto actor_id = actor_id_++;
|
||||||
actors_[actor_id] = td::actor::create_actor<GetTransactionHistory>(
|
actors_[actor_id] = td::actor::create_actor<GetTransactionHistory>(
|
||||||
"GetTransactionHistory", client_.get_client(), account_address, lt, hash, actor_shared(this, actor_id),
|
"GetTransactionHistory", client_.get_client(), account_address, lt, hash, 10, actor_shared(this, actor_id),
|
||||||
promise.wrap([private_key = std::move(private_key)](auto&& x) mutable {
|
promise.wrap([private_key = std::move(private_key)](auto&& x) mutable {
|
||||||
return ToRawTransactions(std::move(private_key)).to_raw_transactions(std::move(x));
|
return ToRawTransactions(std::move(private_key)).to_raw_transactions(std::move(x));
|
||||||
}));
|
}));
|
||||||
return td::Status::OK();
|
return td::Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td::Status TonlibClient::do_request(tonlib_api::raw_getTransactionsV2& request,
|
||||||
|
td::Promise<object_ptr<tonlib_api::raw_transactions>>&& promise) {
|
||||||
|
if (!request.account_address_) {
|
||||||
|
return TonlibError::EmptyField("account_address");
|
||||||
|
}
|
||||||
|
if (!request.from_transaction_id_) {
|
||||||
|
return TonlibError::EmptyField("from_transaction_id");
|
||||||
|
}
|
||||||
|
TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_));
|
||||||
|
td::optional<td::Ed25519::PrivateKey> private_key;
|
||||||
|
if (request.private_key_) {
|
||||||
|
TRY_RESULT(input_key, from_tonlib(*request.private_key_));
|
||||||
|
//NB: optional<Status> has lot of problems. We use emplace to migitate them
|
||||||
|
td::optional<td::Status> o_status;
|
||||||
|
//NB: rely on (and assert) that GetPrivateKey is a synchonous request
|
||||||
|
make_request(int_api::GetPrivateKey{std::move(input_key)}, [&](auto&& r_key) {
|
||||||
|
if (r_key.is_error()) {
|
||||||
|
o_status.emplace(r_key.move_as_error());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
o_status.emplace(td::Status::OK());
|
||||||
|
private_key = td::Ed25519::PrivateKey(std::move(r_key.move_as_ok().private_key));
|
||||||
|
});
|
||||||
|
TRY_STATUS(o_status.unwrap());
|
||||||
|
}
|
||||||
|
auto lt = request.from_transaction_id_->lt_;
|
||||||
|
auto hash_str = request.from_transaction_id_->hash_;
|
||||||
|
if (hash_str.size() != 32) {
|
||||||
|
return td::Status::Error(400, "Invalid transaction id hash size");
|
||||||
|
}
|
||||||
|
td::Bits256 hash;
|
||||||
|
hash.as_slice().copy_from(hash_str);
|
||||||
|
td::int32 count = request.count_ ? request.count_ : 10;
|
||||||
|
|
||||||
|
auto actor_id = actor_id_++;
|
||||||
|
actors_[actor_id] = td::actor::create_actor<GetTransactionHistory>(
|
||||||
|
"GetTransactionHistory", client_.get_client(), account_address, lt, hash, count, actor_shared(this, actor_id),
|
||||||
|
promise.wrap([private_key = std::move(private_key), try_decode_messages = request.try_decode_messages_](auto&& x) mutable {
|
||||||
|
return ToRawTransactions(std::move(private_key), try_decode_messages).to_raw_transactions(std::move(x));
|
||||||
|
}));
|
||||||
|
return td::Status::OK();
|
||||||
|
}
|
||||||
|
|
||||||
td::Status TonlibClient::do_request(const tonlib_api::getAccountState& request,
|
td::Status TonlibClient::do_request(const tonlib_api::getAccountState& request,
|
||||||
td::Promise<object_ptr<tonlib_api::fullAccountState>>&& promise) {
|
td::Promise<object_ptr<tonlib_api::fullAccountState>>&& promise) {
|
||||||
if (!request.account_address_) {
|
if (!request.account_address_) {
|
||||||
|
@ -4223,6 +4586,12 @@ auto to_lite_api(const tonlib_api::ton_blockIdExt& blk) -> td::Result<lite_api_p
|
||||||
blk.workchain_, blk.shard_, blk.seqno_, root_hash, file_hash);
|
blk.workchain_, blk.shard_, blk.seqno_, root_hash, file_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td::Result<ton::BlockIdExt> to_block_id(const tonlib_api::ton_blockIdExt& blk) {
|
||||||
|
TRY_RESULT(root_hash, to_bits256(blk.root_hash_, "blk.root_hash"))
|
||||||
|
TRY_RESULT(file_hash, to_bits256(blk.file_hash_, "blk.file_hash"))
|
||||||
|
return ton::BlockIdExt(blk.workchain_, blk.shard_, blk.seqno_, root_hash, file_hash);
|
||||||
|
}
|
||||||
|
|
||||||
td::Status TonlibClient::do_request(const tonlib_api::getConfigParam& request,
|
td::Status TonlibClient::do_request(const tonlib_api::getConfigParam& request,
|
||||||
td::Promise<object_ptr<tonlib_api::configInfo>>&& promise) {
|
td::Promise<object_ptr<tonlib_api::configInfo>>&& promise) {
|
||||||
TRY_RESULT(lite_block, to_lite_api(*request.id_))
|
TRY_RESULT(lite_block, to_lite_api(*request.id_))
|
||||||
|
@ -4264,20 +4633,21 @@ td::Status TonlibClient::do_request(const tonlib_api::blocks_getShards& request,
|
||||||
td::Promise<object_ptr<tonlib_api::blocks_shards>>&& promise) {
|
td::Promise<object_ptr<tonlib_api::blocks_shards>>&& promise) {
|
||||||
TRY_RESULT(block, to_lite_api(*request.id_))
|
TRY_RESULT(block, to_lite_api(*request.id_))
|
||||||
client_.send_query(ton::lite_api::liteServer_getAllShardsInfo(std::move(block)),
|
client_.send_query(ton::lite_api::liteServer_getAllShardsInfo(std::move(block)),
|
||||||
promise.wrap([](lite_api_ptr<ton::lite_api::liteServer_allShardsInfo>&& all_shards_info) {
|
promise.wrap([](lite_api_ptr<ton::lite_api::liteServer_allShardsInfo>&& all_shards_info)
|
||||||
|
-> td::Result<object_ptr<tonlib_api::blocks_shards>> {
|
||||||
td::BufferSlice proof = std::move((*all_shards_info).proof_);
|
td::BufferSlice proof = std::move((*all_shards_info).proof_);
|
||||||
td::BufferSlice data = std::move((*all_shards_info).data_);
|
td::BufferSlice data = std::move((*all_shards_info).data_);
|
||||||
if (data.empty()) {
|
if (data.empty()) {
|
||||||
//return td::Status::Error("shard configuration is empty");
|
return td::Status::Error("shard configuration is empty");
|
||||||
} else {
|
} else {
|
||||||
auto R = vm::std_boc_deserialize(data.clone());
|
auto R = vm::std_boc_deserialize(data.clone());
|
||||||
if (R.is_error()) {
|
if (R.is_error()) {
|
||||||
//return td::Status::Error("cannot deserialize shard configuration");
|
return R.move_as_error_prefix("cannot deserialize shard configuration: ");
|
||||||
}
|
}
|
||||||
auto root = R.move_as_ok();
|
auto root = R.move_as_ok();
|
||||||
block::ShardConfig sh_conf;
|
block::ShardConfig sh_conf;
|
||||||
if (!sh_conf.unpack(vm::load_cell_slice_ref(root))) {
|
if (!sh_conf.unpack(vm::load_cell_slice_ref(root))) {
|
||||||
//return td::Status::Error("cannot extract shard block list from shard configuration");
|
return td::Status::Error("cannot extract shard block list from shard configuration");
|
||||||
} else {
|
} else {
|
||||||
auto ids = sh_conf.get_shard_hash_ids(true);
|
auto ids = sh_conf.get_shard_hash_ids(true);
|
||||||
tonlib_api::blocks_shards shards;
|
tonlib_api::blocks_shards shards;
|
||||||
|
@ -4424,6 +4794,28 @@ td::Status TonlibClient::do_request(const tonlib_api::blocks_getBlockHeader& req
|
||||||
return td::Status::OK();
|
return td::Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td::Status TonlibClient::do_request(const tonlib_api::blocks_getMasterchainBlockSignatures& request,
|
||||||
|
td::Promise<object_ptr<tonlib_api::blocks_blockSignatures>>&& promise) {
|
||||||
|
auto actor_id = actor_id_++;
|
||||||
|
actors_[actor_id] = td::actor::create_actor<GetMasterchainBlockSignatures>(
|
||||||
|
"GetMasterchainBlockSignatures", client_.get_client(), request.seqno_, actor_shared(this, actor_id),
|
||||||
|
std::move(promise));
|
||||||
|
return td::Status::OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
td::Status TonlibClient::do_request(const tonlib_api::blocks_getShardBlockProof& request,
|
||||||
|
td::Promise<object_ptr<tonlib_api::blocks_shardBlockProof>>&& promise) {
|
||||||
|
TRY_RESULT(id, to_block_id(*request.id_));
|
||||||
|
ton::BlockIdExt from;
|
||||||
|
if (request.mode_ & 1) {
|
||||||
|
TRY_RESULT_ASSIGN(from, to_block_id(*request.id_));
|
||||||
|
}
|
||||||
|
auto actor_id = actor_id_++;
|
||||||
|
actors_[actor_id] = td::actor::create_actor<GetShardBlockProof>("GetShardBlockProof", client_.get_client(), id, from,
|
||||||
|
actor_shared(this, actor_id), std::move(promise));
|
||||||
|
return td::Status::OK();
|
||||||
|
}
|
||||||
|
|
||||||
void TonlibClient::load_libs_from_disk() {
|
void TonlibClient::load_libs_from_disk() {
|
||||||
LOG(DEBUG) << "loading libraries from disk cache";
|
LOG(DEBUG) << "loading libraries from disk cache";
|
||||||
auto r_data = kv_->get("tonlib.libcache");
|
auto r_data = kv_->get("tonlib.libcache");
|
||||||
|
|
|
@ -236,6 +236,8 @@ class TonlibClient : public td::actor::Actor {
|
||||||
td::Promise<object_ptr<tonlib_api::raw_fullAccountState>>&& promise);
|
td::Promise<object_ptr<tonlib_api::raw_fullAccountState>>&& promise);
|
||||||
td::Status do_request(tonlib_api::raw_getTransactions& request,
|
td::Status do_request(tonlib_api::raw_getTransactions& request,
|
||||||
td::Promise<object_ptr<tonlib_api::raw_transactions>>&& promise);
|
td::Promise<object_ptr<tonlib_api::raw_transactions>>&& promise);
|
||||||
|
td::Status do_request(tonlib_api::raw_getTransactionsV2& request,
|
||||||
|
td::Promise<object_ptr<tonlib_api::raw_transactions>>&& promise);
|
||||||
|
|
||||||
td::Status do_request(const tonlib_api::getAccountState& request,
|
td::Status do_request(const tonlib_api::getAccountState& request,
|
||||||
td::Promise<object_ptr<tonlib_api::fullAccountState>>&& promise);
|
td::Promise<object_ptr<tonlib_api::fullAccountState>>&& promise);
|
||||||
|
@ -362,6 +364,10 @@ class TonlibClient : public td::actor::Actor {
|
||||||
td::Promise<object_ptr<tonlib_api::blocks_transactions>>&& promise);
|
td::Promise<object_ptr<tonlib_api::blocks_transactions>>&& promise);
|
||||||
td::Status do_request(const tonlib_api::blocks_getBlockHeader& request,
|
td::Status do_request(const tonlib_api::blocks_getBlockHeader& request,
|
||||||
td::Promise<object_ptr<tonlib_api::blocks_header>>&& promise);
|
td::Promise<object_ptr<tonlib_api::blocks_header>>&& promise);
|
||||||
|
td::Status do_request(const tonlib_api::blocks_getMasterchainBlockSignatures& request,
|
||||||
|
td::Promise<object_ptr<tonlib_api::blocks_blockSignatures>>&& promise);
|
||||||
|
td::Status do_request(const tonlib_api::blocks_getShardBlockProof& request,
|
||||||
|
td::Promise<object_ptr<tonlib_api::blocks_shardBlockProof>>&& promise);
|
||||||
|
|
||||||
td::Status do_request(const tonlib_api::getConfigParam& request,
|
td::Status do_request(const tonlib_api::getConfigParam& request,
|
||||||
td::Promise<object_ptr<tonlib_api::configInfo>>&& promise);
|
td::Promise<object_ptr<tonlib_api::configInfo>>&& promise);
|
||||||
|
|
|
@ -430,6 +430,7 @@ class TonlibCli : public td::actor::Actor {
|
||||||
<< "\t 'e' modifier - encrypt all messages\n"
|
<< "\t 'e' modifier - encrypt all messages\n"
|
||||||
<< "\t 'k' modifier - use fake key\n"
|
<< "\t 'k' modifier - use fake key\n"
|
||||||
<< "\t 'c' modifier - just esmitate fees\n";
|
<< "\t 'c' modifier - just esmitate fees\n";
|
||||||
|
td::TerminalIO::out() << "getmasterchainsignatures <seqno> - get sigratures of masterchain block <seqno>\n";
|
||||||
} else if (cmd == "genkey") {
|
} else if (cmd == "genkey") {
|
||||||
generate_key();
|
generate_key();
|
||||||
} else if (cmd == "exit" || cmd == "quit") {
|
} else if (cmd == "exit" || cmd == "quit") {
|
||||||
|
@ -510,6 +511,9 @@ class TonlibCli : public td::actor::Actor {
|
||||||
auto key = parser.read_word();
|
auto key = parser.read_word();
|
||||||
auto init_key = parser.read_word();
|
auto init_key = parser.read_word();
|
||||||
guess_account(key, init_key, std::move(cmd_promise));
|
guess_account(key, init_key, std::move(cmd_promise));
|
||||||
|
} else if (cmd == "getmasterchainsignatures") {
|
||||||
|
auto seqno = parser.read_word();
|
||||||
|
run_get_masterchain_block_signatures(seqno, std::move(cmd_promise));
|
||||||
} else {
|
} else {
|
||||||
cmd_promise.set_error(td::Status::Error(PSLICE() << "Unkwnown query `" << cmd << "`"));
|
cmd_promise.set_error(td::Status::Error(PSLICE() << "Unkwnown query `" << cmd << "`"));
|
||||||
}
|
}
|
||||||
|
@ -2096,6 +2100,18 @@ class TonlibCli : public td::actor::Actor {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void run_get_masterchain_block_signatures(td::Slice seqno_s, td::Promise<td::Unit> promise) {
|
||||||
|
TRY_RESULT_PROMISE(promise, seqno, td::to_integer_safe<td::int32>(seqno_s));
|
||||||
|
send_query(make_object<tonlib_api::blocks_getMasterchainBlockSignatures>(seqno), promise.wrap([](auto signatures) {
|
||||||
|
td::TerminalIO::out() << "Signatures: " << signatures->signatures_.size() << "\n";
|
||||||
|
for (const auto& s : signatures->signatures_) {
|
||||||
|
td::TerminalIO::out() << " " << s->node_id_short_ << " : " << td::base64_encode(td::Slice(s->signature_))
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
return td::Unit();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
void get_history2(td::Slice key, td::Result<tonlib_api::object_ptr<tonlib_api::fullAccountState>> r_state,
|
void get_history2(td::Slice key, td::Result<tonlib_api::object_ptr<tonlib_api::fullAccountState>> r_state,
|
||||||
td::Promise<td::Unit> promise) {
|
td::Promise<td::Unit> promise) {
|
||||||
TRY_RESULT_PROMISE(promise, state, std::move(r_state));
|
TRY_RESULT_PROMISE(promise, state, std::move(r_state));
|
||||||
|
|
|
@ -1009,6 +1009,56 @@ td::Status ImportShardOverlayCertificateQuery::receive(td::BufferSlice data) {
|
||||||
return td::Status::OK();
|
return td::Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td::Status GetPerfTimerStatsJsonQuery::run() {
|
||||||
|
TRY_RESULT_ASSIGN(file_name_, tokenizer_.get_token<std::string>());
|
||||||
|
TRY_STATUS(tokenizer_.check_endl());
|
||||||
|
return td::Status::OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
td::Status GetPerfTimerStatsJsonQuery::send() {
|
||||||
|
auto b = ton::create_serialize_tl_object<ton::ton_api::engine_validator_getPerfTimerStats>("");
|
||||||
|
td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise());
|
||||||
|
return td::Status::OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
td::Status GetPerfTimerStatsJsonQuery::receive(td::BufferSlice data) {
|
||||||
|
TRY_RESULT_PREFIX(f, ton::fetch_tl_object<ton::ton_api::engine_validator_perfTimerStats>(data.as_slice(), true),
|
||||||
|
"received incorrect answer: ");
|
||||||
|
std::ofstream sb(file_name_);
|
||||||
|
|
||||||
|
sb << "{";
|
||||||
|
bool gtail = false;
|
||||||
|
for (const auto &v : f->stats_) {
|
||||||
|
if (gtail) {
|
||||||
|
sb << ",";
|
||||||
|
} else {
|
||||||
|
gtail = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb << "\n \"" << v->name_ << "\": {";
|
||||||
|
bool tail = false;
|
||||||
|
for (const auto &stat : v->stats_) {
|
||||||
|
if (tail) {
|
||||||
|
sb << ",";
|
||||||
|
} else {
|
||||||
|
tail = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb << "\n \"" << stat->time_ << "\": [";
|
||||||
|
sb << "\n " << stat->min_ << ",";
|
||||||
|
sb << "\n " << stat->avg_ << ",";
|
||||||
|
sb << "\n " << stat->max_;
|
||||||
|
sb << "\n ]";
|
||||||
|
}
|
||||||
|
sb << "\n }";
|
||||||
|
}
|
||||||
|
sb << "\n}\n";
|
||||||
|
sb << std::flush;
|
||||||
|
|
||||||
|
td::TerminalIO::output(std::string("wrote stats to " + file_name_ + "\n"));
|
||||||
|
return td::Status::OK();
|
||||||
|
}
|
||||||
|
|
||||||
td::Status GetValidatorSessionsInfoQuery::run() {
|
td::Status GetValidatorSessionsInfoQuery::run() {
|
||||||
TRY_STATUS(tokenizer_.check_endl());
|
TRY_STATUS(tokenizer_.check_endl());
|
||||||
return td::Status::OK();
|
return td::Status::OK();
|
||||||
|
|
|
@ -1075,6 +1075,28 @@ class ImportShardOverlayCertificateQuery : public Query {
|
||||||
std::string in_file_;
|
std::string in_file_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GetPerfTimerStatsJsonQuery : public Query {
|
||||||
|
public:
|
||||||
|
GetPerfTimerStatsJsonQuery(td::actor::ActorId<ValidatorEngineConsole> console, Tokenizer tokenizer)
|
||||||
|
: Query(console, std::move(tokenizer)) {
|
||||||
|
}
|
||||||
|
td::Status run() override;
|
||||||
|
td::Status send() override;
|
||||||
|
td::Status receive(td::BufferSlice data) override;
|
||||||
|
static std::string get_name() {
|
||||||
|
return "getperftimerstatsjson";
|
||||||
|
}
|
||||||
|
static std::string get_help() {
|
||||||
|
return "getperftimerstatsjson <outfile>\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();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string file_name_;
|
||||||
|
};
|
||||||
|
|
||||||
class GetValidatorSessionsInfoQuery : public Query {
|
class GetValidatorSessionsInfoQuery : public Query {
|
||||||
public:
|
public:
|
||||||
GetValidatorSessionsInfoQuery(td::actor::ActorId<ValidatorEngineConsole> console, Tokenizer tokenizer)
|
GetValidatorSessionsInfoQuery(td::actor::ActorId<ValidatorEngineConsole> console, Tokenizer tokenizer)
|
||||||
|
|
|
@ -140,6 +140,7 @@ void ValidatorEngineConsole::run() {
|
||||||
add_query_runner(std::make_unique<QueryRunnerImpl<GetOverlaysStatsJsonQuery>>());
|
add_query_runner(std::make_unique<QueryRunnerImpl<GetOverlaysStatsJsonQuery>>());
|
||||||
add_query_runner(std::make_unique<QueryRunnerImpl<ImportShardOverlayCertificateQuery>>());
|
add_query_runner(std::make_unique<QueryRunnerImpl<ImportShardOverlayCertificateQuery>>());
|
||||||
add_query_runner(std::make_unique<QueryRunnerImpl<SignShardOverlayCertificateQuery>>());
|
add_query_runner(std::make_unique<QueryRunnerImpl<SignShardOverlayCertificateQuery>>());
|
||||||
|
add_query_runner(std::make_unique<QueryRunnerImpl<GetPerfTimerStatsJsonQuery>>());
|
||||||
add_query_runner(std::make_unique<QueryRunnerImpl<GetValidatorSessionsInfoQuery>>());
|
add_query_runner(std::make_unique<QueryRunnerImpl<GetValidatorSessionsInfoQuery>>());
|
||||||
add_query_runner(std::make_unique<QueryRunnerImpl<AddCollatorQuery>>());
|
add_query_runner(std::make_unique<QueryRunnerImpl<AddCollatorQuery>>());
|
||||||
add_query_runner(std::make_unique<QueryRunnerImpl<AddShardQuery>>());
|
add_query_runner(std::make_unique<QueryRunnerImpl<AddShardQuery>>());
|
||||||
|
|
|
@ -67,9 +67,11 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <limits>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include "git.h"
|
#include "git.h"
|
||||||
|
|
||||||
|
|
||||||
Config::Config() {
|
Config::Config() {
|
||||||
out_port = 3278;
|
out_port = 3278;
|
||||||
full_node = ton::PublicKeyHash::zero();
|
full_node = ton::PublicKeyHash::zero();
|
||||||
|
@ -3355,6 +3357,57 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_getOverla
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_getPerfTimerStats &query, td::BufferSlice data,
|
||||||
|
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise) {
|
||||||
|
if (!(perm & ValidatorEnginePermissions::vep_default)) {
|
||||||
|
promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validator_manager_.empty()) {
|
||||||
|
promise.set_value(
|
||||||
|
create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "validator manager not started")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto P = td::PromiseCreator::lambda(
|
||||||
|
[promise = std::move(promise), query = std::move(query)](td::Result<std::vector<ton::validator::PerfTimerStats>> R) mutable {
|
||||||
|
const std::vector<int> times{60, 300, 3600};
|
||||||
|
double now = td::Time::now();
|
||||||
|
if (R.is_error()) {
|
||||||
|
promise.set_value(create_control_query_error(R.move_as_error()));
|
||||||
|
} else {
|
||||||
|
auto r = R.move_as_ok();
|
||||||
|
std::vector<ton::tl_object_ptr<ton::ton_api::engine_validator_perfTimerStatsByName>> by_name;
|
||||||
|
for (const auto &stats : r) {
|
||||||
|
if (stats.name == query.name_ || query.name_.empty()) {
|
||||||
|
std::vector<ton::tl_object_ptr<ton::ton_api::engine_validator_onePerfTimerStat>> by_time;
|
||||||
|
for (const auto &t : times) {
|
||||||
|
double min = std::numeric_limits<double>::lowest();
|
||||||
|
double max = std::numeric_limits<double>::max();
|
||||||
|
double sum = 0;
|
||||||
|
int cnt = 0;
|
||||||
|
for (const auto &stat : stats.stats) {
|
||||||
|
double time = stat.first;
|
||||||
|
double duration = stat.second;
|
||||||
|
if (now - time <= static_cast<double>(t)) {
|
||||||
|
min = td::min<double>(min, duration);
|
||||||
|
max = td::max<double>(max, duration);
|
||||||
|
sum += duration;
|
||||||
|
++cnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
by_time.push_back(ton::create_tl_object<ton::ton_api::engine_validator_onePerfTimerStat>(t, min, sum / static_cast<double>(cnt), max));
|
||||||
|
}
|
||||||
|
by_name.push_back(ton::create_tl_object<ton::ton_api::engine_validator_perfTimerStatsByName>(stats.name, std::move(by_time)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
promise.set_value(ton::create_serialize_tl_object<ton::ton_api::engine_validator_perfTimerStats>(std::move(by_name)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::prepare_perf_timer_stats, std::move(P));
|
||||||
|
}
|
||||||
|
|
||||||
void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_getValidatorSessionsInfo &query,
|
void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_getValidatorSessionsInfo &query,
|
||||||
td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm,
|
td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm,
|
||||||
td::Promise<td::BufferSlice> promise) {
|
td::Promise<td::BufferSlice> promise) {
|
||||||
|
|
|
@ -437,6 +437,8 @@ class ValidatorEngine : public td::actor::Actor {
|
||||||
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise);
|
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise);
|
||||||
void run_control_query(ton::ton_api::engine_validator_addShard &query, td::BufferSlice data,
|
void run_control_query(ton::ton_api::engine_validator_addShard &query, td::BufferSlice data,
|
||||||
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise);
|
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise);
|
||||||
|
void run_control_query(ton::ton_api::engine_validator_getPerfTimerStats &query, td::BufferSlice data,
|
||||||
|
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise);
|
||||||
template <class T>
|
template <class T>
|
||||||
void run_control_query(T &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm,
|
void run_control_query(T &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm,
|
||||||
td::Promise<td::BufferSlice> promise) {
|
td::Promise<td::BufferSlice> promise) {
|
||||||
|
|
|
@ -167,20 +167,28 @@ void ValidatorSessionDescriptionImpl::update_hash(const RootObject *obj, HashTyp
|
||||||
}
|
}
|
||||||
|
|
||||||
void *ValidatorSessionDescriptionImpl::alloc(size_t size, size_t align, bool temp) {
|
void *ValidatorSessionDescriptionImpl::alloc(size_t size, size_t align, bool temp) {
|
||||||
|
CHECK(align && !(align & (align - 1))); // align should be a power of 2
|
||||||
|
auto get_padding = [&](const uint8_t* ptr) {
|
||||||
|
return (-(size_t)ptr) & (align - 1);
|
||||||
|
};
|
||||||
if (temp) {
|
if (temp) {
|
||||||
|
pdata_temp_ptr_ += get_padding(pdata_temp_ + pdata_temp_ptr_);
|
||||||
auto s = pdata_temp_ptr_;
|
auto s = pdata_temp_ptr_;
|
||||||
pdata_temp_ptr_ += size;
|
pdata_temp_ptr_ += size;
|
||||||
CHECK(s + size <= pdata_temp_size_);
|
CHECK(s + size <= pdata_temp_size_);
|
||||||
return static_cast<void *>(pdata_temp_ + s);
|
return static_cast<void *>(pdata_temp_ + s);
|
||||||
} else {
|
} else {
|
||||||
while (true) {
|
while (true) {
|
||||||
auto s = pdata_perm_ptr_;
|
size_t idx = pdata_perm_ptr_ / pdata_perm_size_;
|
||||||
|
if (idx < pdata_perm_.size()) {
|
||||||
|
auto ptr = pdata_perm_[idx] + (pdata_perm_ptr_ % pdata_perm_size_);
|
||||||
|
pdata_perm_ptr_ += get_padding(ptr);
|
||||||
|
ptr += get_padding(ptr);
|
||||||
pdata_perm_ptr_ += size;
|
pdata_perm_ptr_ += size;
|
||||||
|
|
||||||
if (pdata_perm_ptr_ <= pdata_perm_.size() * pdata_perm_size_) {
|
if (pdata_perm_ptr_ <= pdata_perm_.size() * pdata_perm_size_) {
|
||||||
return static_cast<void *>(pdata_perm_[s / pdata_perm_size_] + (s % pdata_perm_size_));
|
return static_cast<void *>(ptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pdata_perm_.push_back(new td::uint8[pdata_perm_size_]);
|
pdata_perm_.push_back(new td::uint8[pdata_perm_size_]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,10 @@ class ApplyBlock : public td::actor::Actor {
|
||||||
, masterchain_block_id_(masterchain_block_id)
|
, masterchain_block_id_(masterchain_block_id)
|
||||||
, manager_(manager)
|
, manager_(manager)
|
||||||
, timeout_(timeout)
|
, timeout_(timeout)
|
||||||
, promise_(std::move(promise)) {
|
, promise_(std::move(promise))
|
||||||
|
, perf_timer_("applyblock", 0.1, [manager](double duration) {
|
||||||
|
send_closure(manager, &ValidatorManager::add_perf_timer_stat, "applyblock", duration);
|
||||||
|
}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr td::uint32 apply_block_priority() {
|
static constexpr td::uint32 apply_block_priority() {
|
||||||
|
@ -78,7 +81,7 @@ class ApplyBlock : public td::actor::Actor {
|
||||||
BlockHandle handle_;
|
BlockHandle handle_;
|
||||||
td::Ref<ShardState> state_;
|
td::Ref<ShardState> state_;
|
||||||
|
|
||||||
td::PerfWarningTimer perf_timer_{"applyblock", 0.1};
|
td::PerfWarningTimer perf_timer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace validator
|
} // namespace validator
|
||||||
|
|
|
@ -35,7 +35,10 @@ class WaitBlockData : public td::actor::Actor {
|
||||||
, priority_(priority)
|
, priority_(priority)
|
||||||
, manager_(manager)
|
, manager_(manager)
|
||||||
, timeout_(timeout)
|
, timeout_(timeout)
|
||||||
, promise_(std::move(promise)) {
|
, promise_(std::move(promise))
|
||||||
|
, perf_timer_("waitdata", 1.0, [manager](double duration) {
|
||||||
|
send_closure(manager, &ValidatorManager::add_perf_timer_stat, "waitdata", duration);
|
||||||
|
}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_timeout(td::Timestamp timeout, td::uint32 priority) {
|
void update_timeout(td::Timestamp timeout, td::uint32 priority) {
|
||||||
|
@ -74,7 +77,7 @@ class WaitBlockData : public td::actor::Actor {
|
||||||
bool is_hardfork_ = false;
|
bool is_hardfork_ = false;
|
||||||
td::Timestamp try_read_static_file_ = td::Timestamp::now();
|
td::Timestamp try_read_static_file_ = td::Timestamp::now();
|
||||||
|
|
||||||
//td::PerfWarningTimer perf_timer_{"waitdata", 1.0};
|
td::PerfWarningTimer perf_timer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace validator
|
} // namespace validator
|
||||||
|
|
|
@ -34,7 +34,10 @@ class WaitBlockState : public td::actor::Actor {
|
||||||
, manager_(manager)
|
, manager_(manager)
|
||||||
, timeout_(timeout)
|
, timeout_(timeout)
|
||||||
, promise_(std::move(promise))
|
, promise_(std::move(promise))
|
||||||
, persistent_state_desc_(std::move(persistent_state_desc)) {
|
, persistent_state_desc_(std::move(persistent_state_desc))
|
||||||
|
, perf_timer_("waitstate", 1.0, [manager](double duration) {
|
||||||
|
send_closure(manager, &ValidatorManager::add_perf_timer_stat, "waitstate", duration);
|
||||||
|
}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void abort_query(td::Status reason);
|
void abort_query(td::Status reason);
|
||||||
|
@ -83,7 +86,7 @@ class WaitBlockState : public td::actor::Actor {
|
||||||
bool reading_from_db_ = false;
|
bool reading_from_db_ = false;
|
||||||
td::Timestamp next_static_file_attempt_;
|
td::Timestamp next_static_file_attempt_;
|
||||||
|
|
||||||
//td::PerfWarningTimer perf_timer_{"waitstate", 1.0};
|
td::PerfWarningTimer perf_timer_{"waitstate", 1.0};
|
||||||
|
|
||||||
bool check_persistent_state_desc() const {
|
bool check_persistent_state_desc() const {
|
||||||
if (persistent_state_desc_.is_null()) {
|
if (persistent_state_desc_.is_null()) {
|
||||||
|
|
|
@ -54,7 +54,10 @@ AcceptBlockQuery::AcceptBlockQuery(BlockIdExt id, td::Ref<BlockData> data, std::
|
||||||
, send_broadcast_(send_broadcast)
|
, send_broadcast_(send_broadcast)
|
||||||
, apply_(apply)
|
, apply_(apply)
|
||||||
, manager_(manager)
|
, manager_(manager)
|
||||||
, promise_(std::move(promise)) {
|
, promise_(std::move(promise))
|
||||||
|
, perf_timer_("acceptblock", 0.1, [manager](double duration) {
|
||||||
|
send_closure(manager, &ValidatorManager::add_perf_timer_stat, "acceptblock", duration);
|
||||||
|
}) {
|
||||||
state_keep_old_hash_.clear();
|
state_keep_old_hash_.clear();
|
||||||
state_old_hash_.clear();
|
state_old_hash_.clear();
|
||||||
state_hash_.clear();
|
state_hash_.clear();
|
||||||
|
@ -72,7 +75,10 @@ AcceptBlockQuery::AcceptBlockQuery(AcceptBlockQuery::IsFake fake, BlockIdExt id,
|
||||||
, is_fork_(false)
|
, is_fork_(false)
|
||||||
, send_broadcast_(false)
|
, send_broadcast_(false)
|
||||||
, manager_(manager)
|
, manager_(manager)
|
||||||
, promise_(std::move(promise)) {
|
, promise_(std::move(promise))
|
||||||
|
, perf_timer_("acceptblock", 0.1, [manager](double duration) {
|
||||||
|
send_closure(manager, &ValidatorManager::add_perf_timer_stat, "acceptblock", duration);
|
||||||
|
}) {
|
||||||
state_keep_old_hash_.clear();
|
state_keep_old_hash_.clear();
|
||||||
state_old_hash_.clear();
|
state_old_hash_.clear();
|
||||||
state_hash_.clear();
|
state_hash_.clear();
|
||||||
|
@ -87,7 +93,10 @@ AcceptBlockQuery::AcceptBlockQuery(ForceFork ffork, BlockIdExt id, td::Ref<Block
|
||||||
, is_fork_(true)
|
, is_fork_(true)
|
||||||
, send_broadcast_(false)
|
, send_broadcast_(false)
|
||||||
, manager_(manager)
|
, manager_(manager)
|
||||||
, promise_(std::move(promise)) {
|
, promise_(std::move(promise))
|
||||||
|
, perf_timer_("acceptblock", 0.1, [manager](double duration) {
|
||||||
|
send_closure(manager, &ValidatorManager::add_perf_timer_stat, "acceptblock", duration);
|
||||||
|
}) {
|
||||||
state_keep_old_hash_.clear();
|
state_keep_old_hash_.clear();
|
||||||
state_old_hash_.clear();
|
state_old_hash_.clear();
|
||||||
state_hash_.clear();
|
state_hash_.clear();
|
||||||
|
|
|
@ -128,7 +128,7 @@ class AcceptBlockQuery : public td::actor::Actor {
|
||||||
td::BufferSlice top_block_descr_data_;
|
td::BufferSlice top_block_descr_data_;
|
||||||
Ref<ShardTopBlockDescription> top_block_descr_;
|
Ref<ShardTopBlockDescription> top_block_descr_;
|
||||||
|
|
||||||
td::PerfWarningTimer perf_timer_{"acceptblock", 0.1};
|
td::PerfWarningTimer perf_timer_;
|
||||||
|
|
||||||
bool fatal_error(std::string msg, int code = -666);
|
bool fatal_error(std::string msg, int code = -666);
|
||||||
static bool check_send_error(td::actor::ActorId<AcceptBlockQuery> SelfId, td::Status error);
|
static bool check_send_error(td::actor::ActorId<AcceptBlockQuery> SelfId, td::Status error);
|
||||||
|
|
|
@ -47,7 +47,10 @@ class CheckProof : public td::actor::Actor {
|
||||||
, manager_(manager)
|
, manager_(manager)
|
||||||
, timeout_(timeout)
|
, timeout_(timeout)
|
||||||
, promise_(std::move(promise))
|
, promise_(std::move(promise))
|
||||||
, skip_check_signatures_(skip_check_signatures) {
|
, skip_check_signatures_(skip_check_signatures)
|
||||||
|
, perf_timer_("checkproof", 0.1, [manager](double duration) {
|
||||||
|
send_closure(manager, &ValidatorManager::add_perf_timer_stat, "checkproof", duration);
|
||||||
|
}) {
|
||||||
}
|
}
|
||||||
CheckProof(BlockIdExt id, td::Ref<Proof> proof, td::actor::ActorId<ValidatorManager> manager, td::Timestamp timeout,
|
CheckProof(BlockIdExt id, td::Ref<Proof> proof, td::actor::ActorId<ValidatorManager> manager, td::Timestamp timeout,
|
||||||
td::Promise<BlockHandle> promise, bool skip_check_signatures, td::Ref<MasterchainState> known_state)
|
td::Promise<BlockHandle> promise, bool skip_check_signatures, td::Ref<MasterchainState> known_state)
|
||||||
|
@ -58,7 +61,10 @@ class CheckProof : public td::actor::Actor {
|
||||||
, timeout_(timeout)
|
, timeout_(timeout)
|
||||||
, promise_(std::move(promise))
|
, promise_(std::move(promise))
|
||||||
, state_(std::move(known_state))
|
, state_(std::move(known_state))
|
||||||
, skip_check_signatures_(skip_check_signatures) {
|
, skip_check_signatures_(skip_check_signatures)
|
||||||
|
, perf_timer_("checkproof", 0.1, [manager](double duration) {
|
||||||
|
send_closure(manager, &ValidatorManager::add_perf_timer_stat, "checkproof", duration);
|
||||||
|
}) {
|
||||||
}
|
}
|
||||||
CheckProof(BlockIdExt id, td::Ref<ProofLink> proof_link, td::actor::ActorId<ValidatorManager> manager,
|
CheckProof(BlockIdExt id, td::Ref<ProofLink> proof_link, td::actor::ActorId<ValidatorManager> manager,
|
||||||
td::Timestamp timeout, td::Promise<BlockHandle> promise)
|
td::Timestamp timeout, td::Promise<BlockHandle> promise)
|
||||||
|
@ -67,7 +73,10 @@ class CheckProof : public td::actor::Actor {
|
||||||
, proof_(std::move(proof_link))
|
, proof_(std::move(proof_link))
|
||||||
, manager_(manager)
|
, manager_(manager)
|
||||||
, timeout_(timeout)
|
, timeout_(timeout)
|
||||||
, promise_(std::move(promise)) {
|
, promise_(std::move(promise))
|
||||||
|
, perf_timer_("checkproof", 0.1, [manager](double duration) {
|
||||||
|
send_closure(manager, &ValidatorManager::add_perf_timer_stat, "checkproof", duration);
|
||||||
|
}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -114,7 +123,7 @@ class CheckProof : public td::actor::Actor {
|
||||||
bool skip_check_signatures_{false};
|
bool skip_check_signatures_{false};
|
||||||
bool sig_ok_{false};
|
bool sig_ok_{false};
|
||||||
|
|
||||||
td::PerfWarningTimer perf_timer_{"checkproof", 0.1};
|
td::PerfWarningTimer perf_timer_;
|
||||||
|
|
||||||
static bool check_send_error(td::actor::ActorId<CheckProof> SelfId, td::Status error);
|
static bool check_send_error(td::actor::ActorId<CheckProof> SelfId, td::Status error);
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|
|
@ -213,7 +213,7 @@ class Collator final : public td::actor::Actor {
|
||||||
|
|
||||||
std::unique_ptr<ton::BlockCandidate> block_candidate;
|
std::unique_ptr<ton::BlockCandidate> block_candidate;
|
||||||
|
|
||||||
td::PerfWarningTimer perf_timer_{"collate", 0.1};
|
td::PerfWarningTimer perf_timer_;
|
||||||
//
|
//
|
||||||
block::Account* lookup_account(td::ConstBitPtr addr) const;
|
block::Account* lookup_account(td::ConstBitPtr addr) const;
|
||||||
std::unique_ptr<block::Account> make_account_from(td::ConstBitPtr addr, Ref<vm::CellSlice> account,
|
std::unique_ptr<block::Account> make_account_from(td::ConstBitPtr addr, Ref<vm::CellSlice> account,
|
||||||
|
|
|
@ -66,7 +66,10 @@ Collator::Collator(ShardIdFull shard, bool is_hardfork, BlockIdExt min_mastercha
|
||||||
, validator_set_(std::move(validator_set))
|
, validator_set_(std::move(validator_set))
|
||||||
, manager(manager)
|
, manager(manager)
|
||||||
, timeout(timeout)
|
, timeout(timeout)
|
||||||
, main_promise(std::move(promise)) {
|
, main_promise(std::move(promise))
|
||||||
|
, perf_timer_("collate", 0.1, [manager](double duration) {
|
||||||
|
send_closure(manager, &ValidatorManager::add_perf_timer_stat, "collate", duration);
|
||||||
|
}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Collator::start_up() {
|
void Collator::start_up() {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue