mirror of
https://github.com/ton-blockchain/ton
synced 2025-02-12 19:22:37 +00:00
commit
4b940f8bad
106 changed files with 3403 additions and 478 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)
|
||||
|
||||
#BEGIN internal
|
||||
option(USE_EMSCRIPTEN "Use \"ON\" for config building wasm." 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)
|
||||
set(NOT_TON_ONLY_TONLIB false)
|
||||
else()
|
||||
|
@ -245,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")
|
||||
else()
|
||||
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")
|
||||
endif()
|
||||
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 USE_EMSCRIPTEN)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--exclude-libs,ALL")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
|
|
|
@ -188,7 +188,6 @@ void Receiver::receive_from_client(td::IPAddress addr, td::BufferSlice data) {
|
|||
p.data = std::move(data);
|
||||
p.adnl_start_time = start_time();
|
||||
p.seqno = out_seqno_;
|
||||
p.data = std::move(data);
|
||||
|
||||
auto enc = proxy_->encrypt(std::move(p));
|
||||
|
||||
|
|
|
@ -179,6 +179,7 @@ set(FUNC_LIB_SOURCE
|
|||
func/stack-transform.cpp
|
||||
func/optimize.cpp
|
||||
func/codegen.cpp
|
||||
func/func.cpp
|
||||
)
|
||||
|
||||
set(TLB_BLOCK_AUTO
|
||||
|
@ -266,6 +267,8 @@ set(BIGINT_TEST_SOURCE
|
|||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(USE_EMSCRIPTEN ${USE_EMSCRIPTEN} PARENT_SCOPE)
|
||||
|
||||
|
||||
add_library(ton_crypto STATIC ${TON_CRYPTO_SOURCE})
|
||||
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}/..>)
|
||||
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_link_libraries(func PUBLIC ton_crypto src_parser git ton_block)
|
||||
if (WINGETOPT_FOUND)
|
||||
target_link_libraries_system(func wingetopt)
|
||||
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)
|
||||
target_include_directories(tlbc PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
||||
target_link_libraries(tlbc PUBLIC ton_crypto src_parser)
|
||||
|
@ -337,8 +357,9 @@ if (TON_USE_ASAN AND NOT WIN32)
|
|||
endif()
|
||||
|
||||
file(MAKE_DIRECTORY smartcont/auto)
|
||||
if (NOT CMAKE_CROSSCOMPILING)
|
||||
if (NOT CMAKE_CROSSCOMPILING OR USE_EMSCRIPTEN)
|
||||
set(GENERATE_TLB_CMD tlbc)
|
||||
if (NOT USE_EMSCRIPTEN)
|
||||
add_custom_command(
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/block
|
||||
COMMAND ${TURN_OFF_LSAN}
|
||||
|
@ -347,6 +368,15 @@ if (NOT CMAKE_CROSSCOMPILING)
|
|||
OUTPUT ${TLB_BLOCK_AUTO}
|
||||
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_dependencies(ton_block tlb_generate_block)
|
||||
|
||||
|
|
|
@ -982,7 +982,7 @@ struct ShardIdent::Record {
|
|||
int shard_pfx_bits;
|
||||
int workchain_id;
|
||||
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)
|
||||
: shard_pfx_bits(_pfxlen), workchain_id(_wcid), shard_prefix(_pfx) {
|
||||
|
|
|
@ -451,7 +451,7 @@ block_extra in_msg_descr:^InMsgDescr
|
|||
created_by:bits256
|
||||
custom:(Maybe ^McBlockExtra) = BlockExtra;
|
||||
//
|
||||
value_flow ^[ from_prev_blk:CurrencyCollection
|
||||
value_flow#b8e48dfb ^[ from_prev_blk:CurrencyCollection
|
||||
to_next_blk:CurrencyCollection
|
||||
imported:CurrencyCollection
|
||||
exported:CurrencyCollection ]
|
||||
|
|
|
@ -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::ShardAccount::Record acc_info;
|
||||
if (!(block::gen::t_ShardAccount.validate_csr(shard_account) &&
|
||||
block::tlb::t_ShardAccount.validate_csr(shard_account) && tlb::unpack_exact(shard_account.write(), acc_info))) {
|
||||
if (!(block::tlb::t_ShardAccount.validate_csr(shard_account) && tlb::unpack_exact(shard_account.write(), acc_info))) {
|
||||
LOG(ERROR) << "account " << addr.to_hex() << " state is invalid";
|
||||
return false;
|
||||
}
|
||||
|
@ -2013,7 +2012,6 @@ bool Transaction::compute_state() {
|
|||
std::cerr << "new account 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));
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -176,6 +176,7 @@ class AnyIntView {
|
|||
public:
|
||||
enum { word_bits = Tr::word_bits, word_shift = Tr::word_shift };
|
||||
typedef typename Tr::word_t word_t;
|
||||
typedef typename Tr::uword_t uword_t;
|
||||
int& n_;
|
||||
PropagateConstSpan<word_t> digits;
|
||||
|
||||
|
@ -320,7 +321,7 @@ class BigIntG {
|
|||
digits[0] = x;
|
||||
normalize_bool();
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
@ -675,7 +676,7 @@ class BigIntG {
|
|||
return n > 0 && !(digits[0] & 1);
|
||||
}
|
||||
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:
|
||||
|
@ -764,7 +765,7 @@ bool AnyIntView<Tr>::add_pow2_any(int exponent, int factor) {
|
|||
while (size() <= k) {
|
||||
digits[inc_size()] = 0;
|
||||
}
|
||||
digits[k] += ((word_t)factor << dm.rem);
|
||||
digits[k] += factor * ((word_t)1 << dm.rem);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -969,7 +970,7 @@ bool AnyIntView<Tr>::add_mul_any(const AnyIntView<Tr>& yp, const AnyIntView<Tr>&
|
|||
if (hi && hi != -1) {
|
||||
return invalidate_bool();
|
||||
}
|
||||
digits[size() - 1] += (hi << word_shift);
|
||||
digits[size() - 1] += ((uword_t)hi << word_shift);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1014,7 +1015,7 @@ int AnyIntView<Tr>::sgn_un_any() const {
|
|||
}
|
||||
int i = size() - 2;
|
||||
do {
|
||||
v <<= word_shift;
|
||||
v *= Tr::Base;
|
||||
word_t w = digits[i];
|
||||
if (w >= -v + Tr::MaxDenorm) {
|
||||
return 1;
|
||||
|
@ -1059,7 +1060,7 @@ typename Tr::word_t AnyIntView<Tr>::to_long_any() const {
|
|||
} else if (size() == 1) {
|
||||
return digits[0];
|
||||
} 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];
|
||||
w >>= word_shift;
|
||||
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;
|
||||
}
|
||||
while (xn > yn) {
|
||||
v <<= word_shift;
|
||||
v *= Tr::Base;
|
||||
word_t w = T::eval(digits[--xn]);
|
||||
if (w >= -v + Tr::MaxDenorm) {
|
||||
return 1;
|
||||
|
@ -1137,7 +1138,7 @@ int AnyIntView<Tr>::cmp_un_any(const AnyIntView<Tr>& yp) const {
|
|||
return -1;
|
||||
}
|
||||
while (yn > xn) {
|
||||
v <<= word_shift;
|
||||
v *= Tr::Base;
|
||||
word_t w = yp.digits[--yn];
|
||||
if (w <= v - Tr::MaxDenorm) {
|
||||
return 1;
|
||||
|
@ -1150,7 +1151,7 @@ int AnyIntView<Tr>::cmp_un_any(const AnyIntView<Tr>& yp) const {
|
|||
v = 0;
|
||||
}
|
||||
while (--xn >= 0) {
|
||||
v <<= word_shift;
|
||||
v *= Tr::Base;
|
||||
word_t w = T::eval(digits[xn]) - yp.digits[xn];
|
||||
if (w >= -v + Tr::MaxDenorm) {
|
||||
return 1;
|
||||
|
@ -1197,7 +1198,7 @@ int AnyIntView<Tr>::divmod_tiny_any(int y) {
|
|||
}
|
||||
int rem = 0;
|
||||
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;
|
||||
rem = (int)divmod.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);
|
||||
digits[size() - 1] &= Tr::Base - 1;
|
||||
if (!z || z == -1) {
|
||||
digits[size() - 1] += (z << word_shift);
|
||||
digits[size() - 1] += ((uword_t)z << word_shift);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -1338,7 +1339,7 @@ bool AnyIntView<Tr>::mod_div_any(const AnyIntView<Tr>& yp, AnyIntView<Tr>& quot,
|
|||
while (--i >= 0) {
|
||||
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 {
|
||||
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]);
|
||||
}
|
||||
dec_size();
|
||||
digits[size() - 1] += (digits[size()] << word_shift);
|
||||
digits[size() - 1] += ((uword_t)digits[size()] << word_shift);
|
||||
}
|
||||
if (size() >= yp.size() - 1) {
|
||||
assert(size() <= yp.size());
|
||||
|
@ -1455,7 +1456,7 @@ bool AnyIntView<Tr>::mod_pow2_any(int exponent) {
|
|||
dec_size();
|
||||
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);
|
||||
if (!v) {
|
||||
int k = size() - 1;
|
||||
|
@ -1485,7 +1486,7 @@ bool AnyIntView<Tr>::mod_pow2_any(int exponent) {
|
|||
return true;
|
||||
} else if (v >= Tr::Half && size() < max_size()) {
|
||||
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;
|
||||
return true;
|
||||
} else {
|
||||
|
@ -1623,7 +1624,7 @@ bool AnyIntView<Tr>::lshift_any(int exponent) {
|
|||
} else if (v != -1) {
|
||||
return invalidate_bool();
|
||||
} else {
|
||||
digits[size() - 1] += (v << word_shift);
|
||||
digits[size() - 1] += ((uword_t)v << word_shift);
|
||||
}
|
||||
}
|
||||
if (q) {
|
||||
|
@ -1750,7 +1751,7 @@ int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
|
|||
int k = size() - 1;
|
||||
word_t q = digits[k];
|
||||
if (k > 0 && q < Tr::MaxDenorm / 2) {
|
||||
q <<= word_shift;
|
||||
q *= Tr::Base;
|
||||
q += digits[--k];
|
||||
}
|
||||
if (!k) {
|
||||
|
@ -1766,7 +1767,7 @@ int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
|
|||
} else if (q <= -Tr::MaxDenorm / 2) {
|
||||
return s;
|
||||
}
|
||||
q <<= word_shift;
|
||||
q *= Tr::Base;
|
||||
q += digits[--k];
|
||||
}
|
||||
return q >= 0 ? s + 1 : s;
|
||||
|
@ -1774,7 +1775,7 @@ int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
|
|||
int k = size() - 1;
|
||||
word_t q = digits[k];
|
||||
if (k > 0 && q > -Tr::MaxDenorm / 2) {
|
||||
q <<= word_shift;
|
||||
q *= Tr::Base;
|
||||
q += digits[--k];
|
||||
}
|
||||
if (!k) {
|
||||
|
@ -1790,7 +1791,7 @@ int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
|
|||
} else if (q <= -Tr::MaxDenorm / 2) {
|
||||
return s + 1;
|
||||
}
|
||||
q <<= word_shift;
|
||||
q *= Tr::Base;
|
||||
q += digits[--k];
|
||||
}
|
||||
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++) {
|
||||
if ((word_shift & 7) && word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
|
||||
int k1 = 8 - k;
|
||||
v += (digits[i] << k) & 0xff;
|
||||
v += ((uword_t)digits[i] << k) & 0xff;
|
||||
if (ptr > buff) {
|
||||
*--ptr = (unsigned char)(v & 0xff);
|
||||
} 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);
|
||||
k += word_shift - 8;
|
||||
} else {
|
||||
v += (digits[i] << k);
|
||||
v += ((uword_t)digits[i] << k);
|
||||
k += word_shift;
|
||||
}
|
||||
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++) {
|
||||
if ((word_shift & 7) && word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
|
||||
int k1 = 8 - k;
|
||||
v += (digits[i] << k) & 0xff;
|
||||
v += ((uword_t)digits[i] << k) & 0xff;
|
||||
if (buff < end) {
|
||||
*buff++ = (unsigned char)(v & 0xff);
|
||||
} 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);
|
||||
k += word_shift - 8;
|
||||
} else {
|
||||
v += (digits[i] << k);
|
||||
v += ((uword_t)digits[i] << k);
|
||||
k += word_shift;
|
||||
}
|
||||
while (k >= 8) {
|
||||
|
@ -1922,7 +1923,7 @@ bool AnyIntView<Tr>::export_bits_any(unsigned char* buff, int offs, unsigned bit
|
|||
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 {
|
||||
if (!sgnd && v < 0) {
|
||||
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++) {
|
||||
if (word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
|
||||
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);
|
||||
|
@ -1963,7 +1964,7 @@ bool AnyIntView<Tr>::export_bits_any(unsigned char* buff, int offs, unsigned bit
|
|||
v += (digits[i] >> k1);
|
||||
k += word_shift - 8;
|
||||
} else {
|
||||
v += (digits[i] << k);
|
||||
v += ((uword_t)digits[i] << k);
|
||||
k += word_shift;
|
||||
}
|
||||
while (k >= 8) {
|
||||
|
@ -2028,7 +2029,7 @@ bool AnyIntView<Tr>::import_bytes_any(const unsigned char* buff, std::size_t buf
|
|||
return invalidate_bool();
|
||||
}
|
||||
}
|
||||
v |= (((word_t) * --ptr) << k);
|
||||
v |= (((uword_t) * --ptr) << k);
|
||||
k += 8;
|
||||
}
|
||||
if (s) {
|
||||
|
@ -2043,7 +2044,9 @@ bool AnyIntView<Tr>::import_bits_any(const unsigned char* buff, int offs, unsign
|
|||
if (bits < word_shift) {
|
||||
set_size(1);
|
||||
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));
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
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++;
|
||||
}
|
||||
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;
|
||||
if (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;
|
||||
}
|
||||
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;
|
||||
if (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;
|
||||
}
|
||||
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;
|
||||
c = td::count_leading_zeroes32(v);
|
||||
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) {
|
||||
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) {
|
||||
|
|
|
@ -268,3 +268,114 @@ def? config-valid? {
|
|||
config-valid?
|
||||
} cond } cond } cond
|
||||
} : is-valid-config?
|
||||
|
||||
|
||||
// Get anycast depth / rewrite_pfx or return 0
|
||||
// ( S -- x y S )
|
||||
{
|
||||
// maybe
|
||||
1 u@+ swap 0 >
|
||||
{
|
||||
// anycast_info$_ depth:(#<= 30) { depth >= 1 }
|
||||
// rewrite_pfx:(bits depth) = Anycast;
|
||||
30 u@+ swap // get depth
|
||||
|
||||
dup 1 > {
|
||||
dup 2 roll swap u@+ // get rewrite_pfx
|
||||
// return depth, rewrite_pfx, slice
|
||||
}
|
||||
{
|
||||
drop // drop depth (<=1)
|
||||
0 0 2 roll // set anycast to none
|
||||
} cond
|
||||
}
|
||||
{
|
||||
0 0 2 roll // set anycast to none
|
||||
} cond
|
||||
} : maybe-anycast
|
||||
|
||||
// Rewrite first bits of addr with anycast info
|
||||
{ // input: anycast depth, rewrite_pfx, workchain, slice, address length
|
||||
4 -roll
|
||||
3 roll dup dup 0 = { 2drop 2 roll drop }
|
||||
{
|
||||
rot swap u@+ swap drop
|
||||
3 roll
|
||||
<b swap 3 roll u, b> <s swap |+
|
||||
} cond // rewrite first bits of address with rewrite_pfx
|
||||
2 roll
|
||||
u@+ // get address
|
||||
} : parse-address-with-anycast
|
||||
|
||||
// Parse Slice S and return:
|
||||
// 0 `addr_none S - if addr_none$00 is parsed
|
||||
// addr `addr_extern S - if addr_extern$01 is parsed
|
||||
// wc addr `addr_std S - if addr_std$10 is parsed
|
||||
// wc addr `addr_var S - if addr_var$11 is parsed
|
||||
// ( S -- 0 A S or addr A S or wc addr A S )
|
||||
{ 2 u@+ swap dup 0> // Get addr: addr_none$00 / addr_extern$01 / addr_std$10 / addr_var$11
|
||||
{ // if greater that zero
|
||||
dup 1 >
|
||||
{
|
||||
2 =
|
||||
{
|
||||
// if addr_std$10
|
||||
// anycast:(Maybe Anycast)
|
||||
// workchain_id:int8
|
||||
// address:bits256 = MsgAddressInt;
|
||||
maybe-anycast // get anycast depth, bits, slice
|
||||
8 i@+ // get workchain
|
||||
256 parse-address-with-anycast
|
||||
`addr-std swap
|
||||
}
|
||||
|
||||
{
|
||||
// if addr_var$11
|
||||
// anycast:(Maybe Anycast)
|
||||
// addr_len:(## 9)
|
||||
// workchain_id:int32
|
||||
// address:(bits addr_len) = MsgAddressInt;
|
||||
maybe-anycast // get anycast depth, bits, slice
|
||||
9 u@+ // get addr_len
|
||||
32 i@+ // get workchain
|
||||
swap 2 -roll // move workchain to neede position
|
||||
swap parse-address-with-anycast
|
||||
`addr-var swap
|
||||
} cond
|
||||
|
||||
}
|
||||
{
|
||||
drop // drop header (dup for statment upper)
|
||||
// if addr_extern$01
|
||||
// addr_extern$01 len:(## 9)
|
||||
// external_address:(bits len)
|
||||
9 u@+ swap // bit len
|
||||
u@+ // external_address
|
||||
`addr-extern swap
|
||||
} cond
|
||||
}
|
||||
{
|
||||
swap
|
||||
// if addr_none$00
|
||||
`addr-none swap
|
||||
} cond
|
||||
} : addr@+
|
||||
|
||||
{ addr@+ drop } : addr@
|
||||
|
||||
// User-friendly prints output of addr@
|
||||
// (0 A or addr A or wc addr A -- )
|
||||
{
|
||||
dup `addr-none eq?
|
||||
{ 2drop ."addr_none" }
|
||||
{
|
||||
`addr-extern eq?
|
||||
{ (dump) type }
|
||||
{ (x.) swap (dump) ":" $+ swap $+ type }
|
||||
cond
|
||||
}
|
||||
cond
|
||||
} : print-addr // print addr with workchain
|
||||
|
||||
forget maybe-anycast
|
||||
forget parse-address-with-anycast
|
||||
|
|
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (((a | b) & VarDescr::_Nan) || !(~b & (VarDescr::_Neg | VarDescr::_NonZero))) {
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (x.is_int_const() && y.is_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()) {
|
||||
// dubious optimization: NaN * 0 = ?
|
||||
r.set_const(y.int_const);
|
||||
x.unused();
|
||||
return push_const(r.int_const);
|
||||
}
|
||||
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()) {
|
||||
// dubious optimization: NaN * 0 = ?
|
||||
r.set_const(x.int_const);
|
||||
y.unused();
|
||||
return push_const(r.int_const);
|
||||
}
|
||||
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, 0));
|
||||
define_builtin_func("_^>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1));
|
||||
define_builtin_func("_&_", arith_bin_op, AsmOp::Custom("AND", 2));
|
||||
define_builtin_func("_|_", arith_bin_op, AsmOp::Custom("OR", 2));
|
||||
define_builtin_func("_^_", arith_bin_op, AsmOp::Custom("XOR", 2));
|
||||
define_builtin_func("~_", arith_un_op, AsmOp::Custom("NOT", 1));
|
||||
define_builtin_func("_&_", arith_bin_op, compile_and);
|
||||
define_builtin_func("_|_", arith_bin_op, compile_or);
|
||||
define_builtin_func("_^_", arith_bin_op, compile_xor);
|
||||
define_builtin_func("~_", arith_un_op, compile_not);
|
||||
define_builtin_func("^_+=_", arith_bin_op, compile_add);
|
||||
define_builtin_func("^_-=_", arith_bin_op, compile_sub);
|
||||
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, 0));
|
||||
define_builtin_func("^_^>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1));
|
||||
define_builtin_func("^_&=_", arith_bin_op, AsmOp::Custom("AND", 2));
|
||||
define_builtin_func("^_|=_", arith_bin_op, AsmOp::Custom("OR", 2));
|
||||
define_builtin_func("^_^=_", arith_bin_op, AsmOp::Custom("XOR", 2));
|
||||
define_builtin_func("^_&=_", arith_bin_op, compile_and);
|
||||
define_builtin_func("^_|=_", arith_bin_op, compile_or);
|
||||
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("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));
|
||||
|
@ -1063,6 +1184,8 @@ void define_builtins() {
|
|||
AsmOp::Nop());
|
||||
define_builtin_func("~dump", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))),
|
||||
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),
|
||||
[](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)),
|
||||
|
|
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 "parser/srcread.h"
|
||||
#include "parser/lexer.h"
|
||||
#include "parser/symtable.h"
|
||||
#include <getopt.h>
|
||||
#include <fstream>
|
||||
#include "git.h"
|
||||
|
||||
namespace funC {
|
||||
|
||||
int verbosity, indent, opt_level = 2;
|
||||
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;
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
assert(func_val);
|
||||
std::string name = sym::symbols.get_name(func_sym->sym_idx);
|
||||
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) {
|
||||
std::cerr << "( function `" << name << "` undefined )\n";
|
||||
errs << "( function `" << name << "` undefined )\n";
|
||||
throw src::ParseError(func_sym->loc, name);
|
||||
} else {
|
||||
CodeBlob& code = *(func_val->code);
|
||||
if (verbosity >= 3) {
|
||||
code.print(std::cerr, 9);
|
||||
code.print(errs, 9);
|
||||
}
|
||||
code.simplify_var_types();
|
||||
if (verbosity >= 5) {
|
||||
std::cerr << "after simplify_var_types: \n";
|
||||
code.print(std::cerr, 0);
|
||||
errs << "after simplify_var_types: \n";
|
||||
code.print(errs, 0);
|
||||
}
|
||||
code.prune_unreachable_code();
|
||||
if (verbosity >= 5) {
|
||||
std::cerr << "after prune_unreachable: \n";
|
||||
code.print(std::cerr, 0);
|
||||
errs << "after prune_unreachable: \n";
|
||||
code.print(errs, 0);
|
||||
}
|
||||
code.split_vars(true);
|
||||
if (verbosity >= 5) {
|
||||
std::cerr << "after split_vars: \n";
|
||||
code.print(std::cerr, 0);
|
||||
errs << "after split_vars: \n";
|
||||
code.print(errs, 0);
|
||||
}
|
||||
for (int i = 0; i < 8; i++) {
|
||||
code.compute_used_code_vars();
|
||||
if (verbosity >= 4) {
|
||||
std::cerr << "after compute_used_vars: \n";
|
||||
code.print(std::cerr, 6);
|
||||
errs << "after compute_used_vars: \n";
|
||||
code.print(errs, 6);
|
||||
}
|
||||
code.fwd_analyze();
|
||||
if (verbosity >= 5) {
|
||||
std::cerr << "after fwd_analyze: \n";
|
||||
code.print(std::cerr, 6);
|
||||
errs << "after fwd_analyze: \n";
|
||||
code.print(errs, 6);
|
||||
}
|
||||
code.prune_unreachable_code();
|
||||
if (verbosity >= 5) {
|
||||
std::cerr << "after prune_unreachable: \n";
|
||||
code.print(std::cerr, 6);
|
||||
errs << "after prune_unreachable: \n";
|
||||
code.print(errs, 6);
|
||||
}
|
||||
}
|
||||
code.mark_noreturn();
|
||||
if (verbosity >= 3) {
|
||||
code.print(std::cerr, 15);
|
||||
code.print(errs, 15);
|
||||
}
|
||||
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_ref = (func_val->flags & 2);
|
||||
|
@ -107,7 +106,7 @@ void generate_output_func(SymDef* func_sym) {
|
|||
} else if (inline_ref) {
|
||||
modifier = "REF";
|
||||
}
|
||||
*outs << std::string(indent * 2, ' ') << name << " PROC" << modifier << ":<{\n";
|
||||
outs << std::string(indent * 2, ' ') << name << " PROC" << modifier << ":<{\n";
|
||||
int mode = 0;
|
||||
if (stack_layout_comments) {
|
||||
mode |= Stack::_StkCmt | Stack::_CptStkCmt;
|
||||
|
@ -120,145 +119,71 @@ void generate_output_func(SymDef* func_sym) {
|
|||
if (fv && (fv->flags & 1) && code.ops->noreturn()) {
|
||||
mode |= Stack::_InlineFunc;
|
||||
}
|
||||
code.generate_code(*outs, mode, indent + 1);
|
||||
*outs << std::string(indent * 2, ' ') << "}>\n";
|
||||
code.generate_code(outs, mode, indent + 1);
|
||||
outs << std::string(indent * 2, ' ') << "}>\n";
|
||||
if (verbosity >= 2) {
|
||||
std::cerr << "--------------\n";
|
||||
errs << "--------------\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int generate_output() {
|
||||
int generate_output(std::ostream &outs, std::ostream &errs) {
|
||||
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) {
|
||||
*outs << "PROGRAM{\n";
|
||||
outs << "PROGRAM{\n";
|
||||
}
|
||||
for (SymDef* func_sym : glob_func) {
|
||||
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
|
||||
assert(func_val);
|
||||
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()) {
|
||||
*outs << "DECLPROC " << name << "\n";
|
||||
outs << "DECLPROC " << name << "\n";
|
||||
} else {
|
||||
*outs << func_val->method_id << " DECLMETHOD " << name << "\n";
|
||||
outs << func_val->method_id << " DECLMETHOD " << name << "\n";
|
||||
}
|
||||
}
|
||||
for (SymDef* gvar_sym : glob_vars) {
|
||||
assert(dynamic_cast<SymValGlobVar*>(gvar_sym->value));
|
||||
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;
|
||||
for (SymDef* func_sym : glob_func) {
|
||||
try {
|
||||
generate_output_func(func_sym);
|
||||
generate_output_func(func_sym, outs, errs);
|
||||
} 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;
|
||||
++errors;
|
||||
}
|
||||
}
|
||||
if (program_envelope) {
|
||||
*outs << "}END>c\n";
|
||||
outs << "}END>c\n";
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace funC
|
||||
|
||||
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() {
|
||||
void output_inclusion_stack(std::ostream &errs) {
|
||||
while (!funC::inclusion_locations.empty()) {
|
||||
src::SrcLocation loc = funC::inclusion_locations.top();
|
||||
funC::inclusion_locations.pop();
|
||||
if (loc.fdescr) {
|
||||
std::cerr << "note: included from ";
|
||||
loc.show(std::cerr);
|
||||
std::cerr << std::endl;
|
||||
errs << "note: included from ";
|
||||
loc.show(errs);
|
||||
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) {
|
||||
funC::indent = 1;
|
||||
}
|
||||
|
@ -268,12 +193,11 @@ int main(int argc, char* const argv[]) {
|
|||
|
||||
int ok = 0, proc = 0;
|
||||
try {
|
||||
while (optind < argc) {
|
||||
// funC::generated_from += std::string{"`"} + argv[optind] + "` ";
|
||||
ok += funC::parse_source_file(argv[optind++]);
|
||||
for (auto src : sources) {
|
||||
ok += funC::parse_source_file(src.c_str());
|
||||
proc++;
|
||||
}
|
||||
if (interactive) {
|
||||
if (funC::interactive) {
|
||||
funC::generated_from += "stdin ";
|
||||
ok += funC::parse_source_stdin();
|
||||
proc++;
|
||||
|
@ -284,29 +208,24 @@ int main(int argc, char* const argv[]) {
|
|||
if (!proc) {
|
||||
throw src::Fatal{"no source files, no output"};
|
||||
}
|
||||
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 funC::generate_output(outs, errs);
|
||||
} catch (src::Fatal& fatal) {
|
||||
errs << "fatal: " << fatal << std::endl;
|
||||
output_inclusion_stack(errs);
|
||||
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;
|
||||
}
|
||||
funC::outs = fs.get();
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace funC
|
|
@ -39,7 +39,7 @@ extern std::string generated_from;
|
|||
|
||||
constexpr int optimize_depth = 20;
|
||||
|
||||
const std::string func_version{"0.2.0"};
|
||||
const std::string func_version{"0.3.0"};
|
||||
|
||||
enum Keyword {
|
||||
_Eof = -1,
|
||||
|
@ -1631,6 +1631,7 @@ inline compile_func_t make_ext_compile(AsmOp op) {
|
|||
struct SymValAsmFunc : SymValFunc {
|
||||
simple_compile_func_t simple_compile;
|
||||
compile_func_t ext_compile;
|
||||
td::uint64 crc;
|
||||
~SymValAsmFunc() override = default;
|
||||
SymValAsmFunc(TypeExpr* ft, const AsmOp& _macro, bool impure = false)
|
||||
: SymValFunc(-1, ft, impure), simple_compile(make_simple_compile(_macro)) {
|
||||
|
@ -1665,4 +1666,19 @@ AsmOp push_const(td::RefInt256 x);
|
|||
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -254,9 +254,7 @@ void parse_const_decl(Lexer& lex) {
|
|||
if (!sym_def) {
|
||||
lex.cur().error_at("cannot define global symbol `", "`");
|
||||
}
|
||||
if (sym_def->value) {
|
||||
lex.cur().error_at("global symbol `", "` already exists");
|
||||
}
|
||||
Lexem ident = lex.cur();
|
||||
lex.next();
|
||||
if (lex.tp() != '=') {
|
||||
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)) {
|
||||
lex.cur().error("expression type does not match wanted type");
|
||||
}
|
||||
SymValConst* new_value = nullptr;
|
||||
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)
|
||||
sym_def->value = new SymValConst{const_cnt++, x->strval};
|
||||
new_value = new SymValConst{const_cnt++, x->strval};
|
||||
} else if (x->cls == Expr::_Apply) {
|
||||
code.emplace_back(loc, Op::_Import, std::vector<var_idx_t>());
|
||||
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()) {
|
||||
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 {
|
||||
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) {
|
||||
|
@ -1261,19 +1270,48 @@ SymValAsmFunc* parse_asm_func_body(Lexer& lex, TypeExpr* func_type, const Formal
|
|||
lex.expect(')');
|
||||
}
|
||||
while (lex.tp() == _String) {
|
||||
asm_ops.push_back(AsmOp::Parse(lex.cur().str, cnt, width));
|
||||
lex.next();
|
||||
std::string ops = lex.cur().str; // <op>\n<op>\n...
|
||||
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()) {
|
||||
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()) {
|
||||
throw src::ParseError{lex.cur().loc, "string with assembler instruction expected"};
|
||||
}
|
||||
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};
|
||||
res->arg_order = std::move(arg_order);
|
||||
res->ret_order = std::move(ret_order);
|
||||
res->crc = td::crc64(crc_s);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -1439,16 +1477,22 @@ void parse_func_def(Lexer& lex) {
|
|||
// code->print(std::cerr); // !!!DEBUG!!!
|
||||
func_sym_code->code = code;
|
||||
} 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 (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)) {
|
||||
lex.cur().error("redefinition of built-in assembler function `"s + func_name.str + "`");
|
||||
SymValAsmFunc* asm_func_old = dynamic_cast<SymValAsmFunc*>(func_sym_val);
|
||||
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()) {
|
||||
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};
|
||||
}
|
||||
}
|
||||
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 (verbosity >= 2) {
|
||||
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,
|
||||
std::string close_cmts, std::string quote_chars)
|
||||
: src(_src), eof(false), lexem("", src.here(), Lexem::Undefined), peek_lexem("", {}, Lexem::Undefined) {
|
||||
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),
|
||||
multiline_quote(std::move(multiline_quote)) {
|
||||
std::memset(char_class, 0, sizeof(char_class));
|
||||
unsigned char activity = cc::active;
|
||||
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) {
|
||||
if (tp() != exp_tp) {
|
||||
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);
|
||||
}
|
||||
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();
|
||||
const char* end = src.get_ptr();
|
||||
if (is_quote_char(c) || c == '`') {
|
||||
|
|
|
@ -71,6 +71,7 @@ class Lexer {
|
|||
Lexem lexem, peek_lexem;
|
||||
unsigned char char_class[128];
|
||||
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 };
|
||||
|
||||
public:
|
||||
|
@ -78,7 +79,8 @@ class Lexer {
|
|||
return eof;
|
||||
}
|
||||
Lexer(SourceReader& _src, bool init = false, std::string active_chars = ";,() ~.", std::string eol_cmts = ";;",
|
||||
std::string open_cmts = "{-", std::string close_cmts = "-}", std::string quote_chars = "\"");
|
||||
std::string open_cmts = "{-", std::string close_cmts = "-}", std::string quote_chars = "\"",
|
||||
std::string multiline_quote = "\"\"\"");
|
||||
const Lexem& next();
|
||||
const Lexem& cur() const {
|
||||
return lexem;
|
||||
|
@ -109,6 +111,7 @@ class Lexer {
|
|||
|
||||
private:
|
||||
void set_spec(std::array<int, 3>& arr, std::string setup);
|
||||
bool is_multiline_quote(const char* begin, const char* end);
|
||||
};
|
||||
|
||||
} // namespace src
|
||||
|
|
|
@ -2423,7 +2423,7 @@ std::vector<const src::FileDescr*> source_fdescr;
|
|||
|
||||
bool parse_source(std::istream* is, src::FileDescr* fdescr) {
|
||||
src::SourceReader reader{is, fdescr};
|
||||
src::Lexer lex{reader, true, "(){}:;? #$. ^~ #", "//", "/*", "*/"};
|
||||
src::Lexer lex{reader, true, "(){}:;? #$. ^~ #", "//", "/*", "*/", ""};
|
||||
while (lex.tp() != src::_Eof) {
|
||||
parse_constructor_def(lex);
|
||||
// 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 {
|
||||
if (ops && --*ops < 0) {
|
||||
if (ops) {
|
||||
if (*ops <= 0) {
|
||||
return false;
|
||||
}
|
||||
--*ops;
|
||||
}
|
||||
bool is_special;
|
||||
auto cs = load_cell_slice_special(std::move(cell_ref), is_special);
|
||||
return always_special() ? is_special : (is_special ? weak : (validate_skip(ops, cs) && cs.empty_ext()));
|
||||
|
|
|
@ -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) {
|
||||
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) {
|
||||
|
|
|
@ -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
|
||||
// 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();
|
||||
if (vm_state_interface) {
|
||||
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);
|
||||
auto library_cell = vm_state_interface->load_library(cs.data_bits() + 8);
|
||||
if (library_cell.not_null()) {
|
||||
//TODO: fix infinity loop
|
||||
return load_cell_slice_impl(library_cell, nullptr);
|
||||
cell = library_cell;
|
||||
can_be_special = nullptr;
|
||||
continue;
|
||||
}
|
||||
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"};
|
||||
}
|
||||
return loaded_cell;
|
||||
}
|
||||
}
|
||||
|
||||
CellSlice load_cell_slice(const Ref<Cell>& cell) {
|
||||
|
|
|
@ -105,6 +105,43 @@ int exec_dump_value(VmState* st, unsigned arg) {
|
|||
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) {
|
||||
using namespace std::placeholders;
|
||||
if (!vm_debug_enabled) {
|
||||
|
@ -113,7 +150,9 @@ void register_debug_ops(OpcodeTable& cp0) {
|
|||
} else {
|
||||
// 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))
|
||||
.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::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));
|
||||
|
|
|
@ -313,7 +313,7 @@ td::Status LargeBocSerializer::serialize(td::FileFd& fd, int mode) {
|
|||
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) {
|
||||
writer.store_uint(value, info.ref_byte_size);
|
||||
};
|
||||
|
|
|
@ -90,15 +90,27 @@ void StackEntry::dump(std::ostream& os) const {
|
|||
os << dec_string(as_int());
|
||||
break;
|
||||
case t_cell:
|
||||
if (ref.not_null()) {
|
||||
os << "C{" << static_cast<Ref<Cell>>(ref)->get_hash().to_hex() << "}";
|
||||
} else {
|
||||
os << "C{null}";
|
||||
}
|
||||
break;
|
||||
case t_builder:
|
||||
if (ref.not_null()) {
|
||||
os << "BC{" << static_cast<Ref<CellBuilder>>(ref)->to_hex() << "}";
|
||||
} else {
|
||||
os << "BC{null}";
|
||||
}
|
||||
break;
|
||||
case t_slice: {
|
||||
if (ref.not_null()) {
|
||||
os << "CS{";
|
||||
static_cast<Ref<CellSlice>>(ref)->dump(os, 1, false);
|
||||
os << '}';
|
||||
} else {
|
||||
os << "CS{null}";
|
||||
}
|
||||
break;
|
||||
}
|
||||
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);
|
||||
auto x = stack.pop_int();
|
||||
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)) {
|
||||
throw VmError{Excno::range_chk};
|
||||
}
|
||||
|
|
|
@ -503,7 +503,7 @@ int VmState::run() {
|
|||
|
||||
bool VmState::try_commit() {
|
||||
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.c5 = cr.d[1];
|
||||
cstate.committed = true;
|
||||
|
|
36
fec/fec.cpp
36
fec/fec.cpp
|
@ -19,6 +19,7 @@
|
|||
#include "fec.h"
|
||||
#include "td/utils/overloaded.h"
|
||||
#include "auto/tl/ton_api.hpp"
|
||||
#include "td/utils/misc.h"
|
||||
|
||||
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::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;
|
||||
ton_api::downcast_call(
|
||||
*obj.get(), td::overloaded(
|
||||
ton_api::downcast_call(*obj,
|
||||
td::overloaded(
|
||||
[&](const ton_api::fec_raptorQ &obj) {
|
||||
T.type_ = td::fec::RaptorQEncoder::Parameters{static_cast<size_t>(obj.data_size_),
|
||||
static_cast<size_t>(obj.symbol_size_),
|
||||
static_cast<size_t>(obj.symbols_count_)};
|
||||
T.type_ = td::fec::RaptorQEncoder::Parameters{data_size, symbol_size, symbols_count};
|
||||
},
|
||||
[&](const ton_api::fec_roundRobin &obj) {
|
||||
T.type_ = td::fec::RoundRobinEncoder::Parameters{static_cast<size_t>(obj.data_size_),
|
||||
static_cast<size_t>(obj.symbol_size_),
|
||||
static_cast<size_t>(obj.symbols_count_)};
|
||||
T.type_ = td::fec::RoundRobinEncoder::Parameters{data_size, symbol_size, symbols_count};
|
||||
},
|
||||
[&](const ton_api::fec_online &obj) {
|
||||
T.type_ = td::fec::OnlineEncoder::Parameters{static_cast<size_t>(obj.data_size_),
|
||||
static_cast<size_t>(obj.symbol_size_),
|
||||
static_cast<size_t>(obj.symbols_count_)};
|
||||
T.type_ = td::fec::OnlineEncoder::Parameters{data_size, symbol_size, symbols_count};
|
||||
}));
|
||||
return T;
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ td::Result<std::unique_ptr<BroadcastFec>> BroadcastFec::create(Overlay::Broadcas
|
|||
Overlay::BroadcastDataHash data_hash, td::uint32 flags,
|
||||
td::uint32 date, fec::FecType fec_type) {
|
||||
auto F = std::make_unique<BroadcastFec>(hash, std::move(src), data_hash, flags, date, std::move(fec_type));
|
||||
TRY_STATUS(F->init_fec_type());
|
||||
TRY_STATUS(F->run_checks());
|
||||
TRY_STATUS(F->init_fec_type());
|
||||
return std::move(F);
|
||||
}
|
||||
|
||||
|
|
|
@ -192,7 +192,7 @@ class OverlayImpl : public Overlay {
|
|||
} else {
|
||||
std::vector<adnl::AdnlNodeIdShort> vec;
|
||||
for (td::uint32 i = 0; i < max_size; i++) {
|
||||
vec.push_back(neighbours_[td::Random::fast(0, static_cast<td::int32>(neighbours_.size()))]);
|
||||
vec.push_back(neighbours_[td::Random::fast(0, static_cast<td::int32>(neighbours_.size()) - 1)]);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
|
|
@ -125,6 +125,10 @@ void RldpTransferReceiverImpl::receive_part(fec::FecType fec_type, td::uint32 pa
|
|||
}
|
||||
|
||||
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();
|
||||
if (D.is_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());
|
||||
}
|
||||
|
||||
PerfWarningTimer::PerfWarningTimer(string name, double max_duration)
|
||||
: name_(std::move(name)), start_at_(Time::now()), max_duration_(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), callback_(std::move(callback)) {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -78,8 +78,9 @@ void PerfWarningTimer::reset() {
|
|||
return;
|
||||
}
|
||||
double duration = Time::now() - start_at_;
|
||||
LOG_IF(WARNING, duration > max_duration_)
|
||||
<< "SLOW: " << tag("name", name_) << tag("duration", format::as_time(duration));
|
||||
//LOG_IF(WARNING, duration > max_duration_)
|
||||
//<< "SLOW: " << tag("name", name_) << tag("duration", format::as_time(duration));
|
||||
callback_(duration);
|
||||
start_at_ = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace td {
|
||||
|
||||
class Timer {
|
||||
|
@ -44,7 +46,7 @@ class Timer {
|
|||
|
||||
class PerfWarningTimer {
|
||||
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 &operator=(const PerfWarningTimer &) = delete;
|
||||
PerfWarningTimer(PerfWarningTimer &&other);
|
||||
|
@ -56,6 +58,7 @@ class PerfWarningTimer {
|
|||
string name_;
|
||||
double start_at_{0};
|
||||
double max_duration_{0};
|
||||
std::function<void(double)> callback_;
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
#define TD_EVENTFD_BSD 1
|
||||
#elif TD_EMSCRIPTEN
|
||||
#define TD_POLL_POLL 1
|
||||
#define TD_EVENTFD_UNSUPPORTED 1
|
||||
// #define TD_EVENTFD_UNSUPPORTED 1
|
||||
#elif TD_DARWIN
|
||||
#define TD_POLL_KQUEUE 1
|
||||
#define TD_EVENTFD_BSD 1
|
||||
|
@ -51,7 +51,11 @@
|
|||
#endif
|
||||
|
||||
#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
|
||||
#define TD_THREAD_PTHREAD 1
|
||||
#else
|
||||
|
|
|
@ -76,7 +76,8 @@ class TestNode : public td::actor::Actor {
|
|||
void on_block_committed(td::uint32 round, ton::PublicKeyHash src,
|
||||
ton::validatorsession::ValidatorSessionRootHash root_hash,
|
||||
ton::validatorsession::ValidatorSessionFileHash file_hash, td::BufferSlice data,
|
||||
std::vector<std::pair<ton::PublicKeyHash, td::BufferSlice>> signatures) override {
|
||||
std::vector<std::pair<ton::PublicKeyHash, td::BufferSlice>> signatures,
|
||||
ton::validatorsession::ValidatorSessionStats stats) override {
|
||||
td::actor::send_closure(id_, &TestNode::on_block_committed, round, root_hash, std::move(data),
|
||||
std::move(signatures));
|
||||
}
|
||||
|
|
|
@ -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.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.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;
|
||||
|
||||
|
@ -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.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.getShardBlockProof id:tonNode.blockIdExt = liteServer.ShardBlockProof;
|
||||
|
||||
liteServer.queryPrefix = Object;
|
||||
liteServer.query data:bytes = Object;
|
||||
|
|
Binary file not shown.
|
@ -630,6 +630,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.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;
|
||||
|
||||
|
||||
---functions---
|
||||
|
||||
|
@ -680,6 +684,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.importShardOverlayCertificate workchain:int shard:long signed_key:engine.validator.KeyHash cert:overlay.Certificate = engine.validator.Success;
|
||||
|
||||
engine.validator.getPerfTimerStats name:string = engine.validator.PerfTimerStats;
|
||||
|
||||
---types---
|
||||
|
||||
storage.pong = storage.Pong;
|
||||
|
@ -721,3 +727,13 @@ http.server.host domains:(vector string) ip:int port:int adnl_id:adnl.id.short =
|
|||
http.server.config dhs:(vector http.server.dnsEntry) local_hosts:(vector http.server.host) = http.server.Config;
|
||||
|
||||
---functions---
|
||||
|
||||
---types---
|
||||
|
||||
validatorSession.statsProducer id:int256 block_status:int block_timestamp:long = validatorSession.StatsProducer;
|
||||
|
||||
validatorSession.statsRound timestamp:long producers:(vector validatorSession.statsProducer) = validatorSession.StatsRound;
|
||||
|
||||
validatorSession.stats id:tonNode.blockId timestamp:long self:int256 creator:int256 total_validators:int total_weight:long
|
||||
signatures:int signatures_weight:long approve_signatures:int approve_signatures_weight:long
|
||||
first_round:int rounds:(vector validatorSession.statsRound) = validatorSession.Stats;
|
||||
|
|
Binary file not shown.
|
@ -55,7 +55,7 @@ raw.message source:accountAddress destination:accountAddress value:int64 fwd_fee
|
|||
raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 storage_fee:int64 other_fee:int64 in_msg:raw.message out_msgs:vector<raw.message> = raw.Transaction;
|
||||
raw.transactions transactions:vector<raw.transaction> previous_transaction_id:internal.transactionId = raw.Transactions;
|
||||
|
||||
raw.extMessageInfo body_hash:bytes = raw.ExtMessageInfo;
|
||||
raw.extMessageInfo hash:bytes = raw.ExtMessageInfo;
|
||||
|
||||
pchan.config alice_public_key:string alice_address:accountAddress bob_public_key:string bob_address:accountAddress init_timeout:int32 close_timeout:int32 channel_id:int64 = pchan.Config;
|
||||
|
||||
|
@ -180,6 +180,9 @@ smc.methodIdName name:string = smc.MethodId;
|
|||
|
||||
smc.runResult gas_used:int53 stack:vector<tvm.StackEntry> exit_code:int32 = smc.RunResult;
|
||||
|
||||
smc.libraryEntry hash:int256 data:bytes = smc.LibraryEntry;
|
||||
smc.libraryResult result:(vector smc.libraryEntry) = smc.LibraryResult;
|
||||
|
||||
updateSendLiteServerQuery id:int64 data:bytes = Update;
|
||||
updateSyncState sync_state:SyncState = Update;
|
||||
|
||||
|
@ -214,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.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;
|
||||
|
||||
---functions---
|
||||
|
@ -248,7 +257,9 @@ getBip39Hints prefix:string = Bip39Hints;
|
|||
//raw.init initial_account_state:raw.initialAccountState = Ok;
|
||||
raw.getAccountState account_address:accountAddress = raw.FullAccountState;
|
||||
raw.getTransactions private_key:InputKey account_address:accountAddress from_transaction_id:internal.transactionId = raw.Transactions;
|
||||
raw.sendMessage body:bytes = raw.ExtMessageInfo;
|
||||
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.sendMessageReturnHash body:bytes = raw.ExtMessageInfo;
|
||||
raw.createAndSendMessage destination:accountAddress initial_account_state:bytes data:bytes = Ok;
|
||||
raw.createQuery destination:accountAddress init_code:bytes init_data:bytes body:bytes = query.Info;
|
||||
|
||||
|
@ -286,6 +297,8 @@ smc.getData id:int53 = tvm.Cell;
|
|||
smc.getState id:int53 = tvm.Cell;
|
||||
smc.runGetMethod id:int53 method:smc.MethodId stack:vector<tvm.StackEntry> = smc.RunResult;
|
||||
|
||||
smc.getLibraries library_list:(vector int256) = smc.LibraryResult;
|
||||
|
||||
dns.resolve account_address:accountAddress name:string category:int256 ttl:int32 = dns.Resolved;
|
||||
|
||||
pchan.signPromise input_key:InputKey promise:pchan.promise = pchan.Promise;
|
||||
|
@ -300,6 +313,8 @@ blocks.getShards id:ton.blockIdExt = blocks.Shards;
|
|||
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.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;
|
||||
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_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})
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
generate_export_header(tonlibjson EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/tonlib/tonlibjson_export.h)
|
||||
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(EXPORT Tonlib
|
||||
if (NOT USE_EMSCRIPTEN)
|
||||
install(EXPORT Tonlib
|
||||
FILE TonlibTargets.cmake
|
||||
NAMESPACE Tonlib::
|
||||
DESTINATION lib/cmake/Tonlib
|
||||
)
|
||||
)
|
||||
endif()
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file("TonlibConfigVersion.cmake"
|
||||
VERSION ${TON_VERSION}
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "block/check-proof.h"
|
||||
#include "ton/lite-tl.hpp"
|
||||
#include "ton/ton-shard.h"
|
||||
#include "lite-client/lite-client-common.h"
|
||||
|
||||
#include "vm/boc.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 {
|
||||
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)
|
||||
: address_(std::move(address))
|
||||
, lt_(std::move(lt))
|
||||
, hash_(std::move(hash))
|
||||
, count_(count)
|
||||
, parent_(std::move(parent))
|
||||
, promise_(std::move(promise)) {
|
||||
client_.set_client(ext_client_ref);
|
||||
|
@ -975,7 +977,7 @@ class GetTransactionHistory : public td::actor::Actor {
|
|||
ton::LogicalTime lt_;
|
||||
ton::Bits256 hash_;
|
||||
ExtClient client_;
|
||||
td::int32 count_{10};
|
||||
td::int32 count_;
|
||||
td::actor::ActorShared<> parent_;
|
||||
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() = 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)));
|
||||
}
|
||||
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_;
|
||||
bool try_decode_messages_;
|
||||
|
||||
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;
|
||||
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 {
|
||||
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);
|
||||
td::Status status;
|
||||
|
||||
|
@ -2203,7 +2524,6 @@ struct ToRawTransactions {
|
|||
if (type == 0) {
|
||||
data = tonlib_api::make_object<tonlib_api::msg_dataText>(r_body_message.move_as_ok());
|
||||
} else {
|
||||
LOG(ERROR) << "TRY DECRYPT";
|
||||
auto encrypted_message = r_body_message.move_as_ok();
|
||||
auto r_decrypted_message = [&]() -> td::Result<std::string> {
|
||||
if (!private_key_) {
|
||||
|
@ -2366,15 +2686,22 @@ auto to_any_promise(td::Promise<td::Unit>&& promise) {
|
|||
}
|
||||
|
||||
td::Status TonlibClient::do_request(const tonlib_api::raw_sendMessage& request,
|
||||
td::Promise<object_ptr<tonlib_api::raw_extMessageInfo>>&& promise) {
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
|
||||
TRY_RESULT_PREFIX(body, vm::std_boc_deserialize(request.body_), TonlibError::InvalidBagOfCells("body"));
|
||||
std::ostringstream os;
|
||||
block::gen::t_Message_Any.print_ref(os, body);
|
||||
LOG(ERROR) << os.str();
|
||||
auto body_hash = body->get_hash().as_slice().str();
|
||||
make_request(int_api::SendMessage{std::move(body)}, to_any_promise(std::move(promise)));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(const tonlib_api::raw_sendMessageReturnHash& request,
|
||||
td::Promise<object_ptr<tonlib_api::raw_extMessageInfo>>&& promise) {
|
||||
TRY_RESULT_PREFIX(body, vm::std_boc_deserialize(request.body_), TonlibError::InvalidBagOfCells("body"));
|
||||
auto hash = body->get_hash().as_slice().str();
|
||||
make_request(int_api::SendMessage{std::move(body)},
|
||||
promise.wrap([body_hash = std::move(body_hash)](auto res) {
|
||||
return tonlib_api::make_object<tonlib_api::raw_extMessageInfo>(std::move(body_hash));
|
||||
promise.wrap([hash = std::move(hash)](auto res) {
|
||||
return tonlib_api::make_object<tonlib_api::raw_extMessageInfo>(std::move(hash));
|
||||
}));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
@ -2457,13 +2784,56 @@ td::Status TonlibClient::do_request(tonlib_api::raw_getTransactions& request,
|
|||
|
||||
auto actor_id = actor_id_++;
|
||||
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 {
|
||||
return ToRawTransactions(std::move(private_key)).to_raw_transactions(std::move(x));
|
||||
}));
|
||||
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::Promise<object_ptr<tonlib_api::fullAccountState>>&& promise) {
|
||||
if (!request.account_address_) {
|
||||
|
@ -3438,7 +3808,7 @@ td::Result<vm::StackEntry> from_tonlib_api(tonlib_api::tvm_StackEntry& entry) {
|
|||
}
|
||||
|
||||
void deep_library_search(std::set<td::Bits256>& set, std::set<vm::Cell::Hash>& visited,
|
||||
vm::Dictionary libs, td::Ref<vm::Cell> cell, int depth) {
|
||||
vm::Dictionary& libs, td::Ref<vm::Cell> cell, int depth) {
|
||||
if (depth <= 0 || set.size() >= 16 || visited.size() >= 256) {
|
||||
return;
|
||||
}
|
||||
|
@ -3469,6 +3839,58 @@ void deep_library_search(std::set<td::Bits256>& set, std::set<vm::Cell::Hash>& v
|
|||
}
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(const tonlib_api::smc_getLibraries& request,
|
||||
td::Promise<object_ptr<tonlib_api::smc_libraryResult>>&& promise) {
|
||||
std::vector<object_ptr<tonlib_api::smc_libraryEntry>> result_entries;
|
||||
result_entries.reserve(request.library_list_.size());
|
||||
std::vector<td::Bits256> not_cached_hashes;
|
||||
for (auto& library_hash : request.library_list_) {
|
||||
if (libraries.key_exists(library_hash)) {
|
||||
auto library_content = vm::std_boc_serialize(libraries.lookup_ref(library_hash)).move_as_ok().as_slice().str();
|
||||
result_entries.push_back(tonlib_api::make_object<tonlib_api::smc_libraryEntry>(library_hash, library_content));
|
||||
} else {
|
||||
not_cached_hashes.push_back(library_hash);
|
||||
}
|
||||
}
|
||||
|
||||
if (not_cached_hashes.empty()) {
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::smc_libraryResult>(std::move(result_entries)));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
client_.send_query(ton::lite_api::liteServer_getLibraries(std::move(not_cached_hashes)),
|
||||
promise.wrap([self=this, result_entries = std::move(result_entries)]
|
||||
(td::Result<ton::lite_api::object_ptr<ton::lite_api::liteServer_libraryResult>> r_libraries) mutable
|
||||
{
|
||||
if (r_libraries.is_error()) {
|
||||
LOG(WARNING) << "cannot obtain found libraries: " << r_libraries.move_as_error().to_string();
|
||||
} else {
|
||||
auto libraries = r_libraries.move_as_ok();
|
||||
bool updated = false;
|
||||
for (auto& lr : libraries->result_) {
|
||||
auto contents = vm::std_boc_deserialize(lr->data_);
|
||||
if (contents.is_ok() && contents.ok().not_null()) {
|
||||
if (contents.ok()->get_hash().bits().compare(lr->hash_.cbits(), 256)) {
|
||||
LOG(WARNING) << "hash mismatch for library " << lr->hash_.to_hex();
|
||||
continue;
|
||||
}
|
||||
result_entries.push_back(tonlib_api::make_object<tonlib_api::smc_libraryEntry>(lr->hash_, lr->data_.as_slice().str()));
|
||||
self->libraries.set_ref(lr->hash_, contents.move_as_ok());
|
||||
updated = true;
|
||||
LOG(DEBUG) << "registered library " << lr->hash_.to_hex();
|
||||
} else {
|
||||
LOG(WARNING) << "failed to deserialize library: " << lr->hash_.to_hex();
|
||||
}
|
||||
if (updated) {
|
||||
self->store_libs_to_disk();
|
||||
}
|
||||
}
|
||||
}
|
||||
return tonlib_api::make_object<tonlib_api::smc_libraryResult>(std::move(result_entries));
|
||||
}));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(const tonlib_api::smc_runGetMethod& request,
|
||||
td::Promise<object_ptr<tonlib_api::smc_runResult>>&& promise) {
|
||||
auto it = smcs_.find(request.id_);
|
||||
|
@ -4164,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);
|
||||
}
|
||||
|
||||
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::Promise<object_ptr<tonlib_api::configInfo>>&& promise) {
|
||||
TRY_RESULT(lite_block, to_lite_api(*request.id_))
|
||||
|
@ -4205,20 +4633,21 @@ td::Status TonlibClient::do_request(const tonlib_api::blocks_getShards& request,
|
|||
td::Promise<object_ptr<tonlib_api::blocks_shards>>&& promise) {
|
||||
TRY_RESULT(block, to_lite_api(*request.id_))
|
||||
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 data = std::move((*all_shards_info).data_);
|
||||
if (data.empty()) {
|
||||
//return td::Status::Error("shard configuration is empty");
|
||||
return td::Status::Error("shard configuration is empty");
|
||||
} else {
|
||||
auto R = vm::std_boc_deserialize(data.clone());
|
||||
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();
|
||||
block::ShardConfig sh_conf;
|
||||
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 {
|
||||
auto ids = sh_conf.get_shard_hash_ids(true);
|
||||
tonlib_api::blocks_shards shards;
|
||||
|
@ -4365,6 +4794,28 @@ td::Status TonlibClient::do_request(const tonlib_api::blocks_getBlockHeader& req
|
|||
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() {
|
||||
LOG(DEBUG) << "loading libraries from disk cache";
|
||||
auto r_data = kv_->get("tonlib.libcache");
|
||||
|
|
|
@ -224,7 +224,9 @@ class TonlibClient : public td::actor::Actor {
|
|||
td::Status do_request(tonlib_api::options_setConfig& request,
|
||||
td::Promise<object_ptr<tonlib_api::options_configInfo>>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::raw_sendMessage& request, td::Promise<object_ptr<tonlib_api::raw_extMessageInfo>>&& promise);
|
||||
td::Status do_request(const tonlib_api::raw_sendMessage& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(const tonlib_api::raw_sendMessageReturnHash& request,
|
||||
td::Promise<object_ptr<tonlib_api::raw_extMessageInfo>>&& promise);
|
||||
td::Status do_request(const tonlib_api::raw_createAndSendMessage& request,
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(const tonlib_api::raw_createQuery& request,
|
||||
|
@ -234,6 +236,8 @@ class TonlibClient : public td::actor::Actor {
|
|||
td::Promise<object_ptr<tonlib_api::raw_fullAccountState>>&& promise);
|
||||
td::Status do_request(tonlib_api::raw_getTransactions& request,
|
||||
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::Promise<object_ptr<tonlib_api::fullAccountState>>&& promise);
|
||||
|
@ -311,6 +315,9 @@ class TonlibClient : public td::actor::Actor {
|
|||
td::Status do_request(const tonlib_api::smc_runGetMethod& request,
|
||||
td::Promise<object_ptr<tonlib_api::smc_runResult>>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::smc_getLibraries& request,
|
||||
td::Promise<object_ptr<tonlib_api::smc_libraryResult>>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::dns_resolve& request,
|
||||
td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise);
|
||||
|
||||
|
@ -357,6 +364,10 @@ class TonlibClient : public td::actor::Actor {
|
|||
td::Promise<object_ptr<tonlib_api::blocks_transactions>>&& promise);
|
||||
td::Status do_request(const tonlib_api::blocks_getBlockHeader& request,
|
||||
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::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 'k' modifier - use fake key\n"
|
||||
<< "\t 'c' modifier - just esmitate fees\n";
|
||||
td::TerminalIO::out() << "getmasterchainsignatures <seqno> - get sigratures of masterchain block <seqno>\n";
|
||||
} else if (cmd == "genkey") {
|
||||
generate_key();
|
||||
} else if (cmd == "exit" || cmd == "quit") {
|
||||
|
@ -510,6 +511,9 @@ class TonlibCli : public td::actor::Actor {
|
|||
auto key = parser.read_word();
|
||||
auto init_key = parser.read_word();
|
||||
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 {
|
||||
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,
|
||||
td::Promise<td::Unit> promise) {
|
||||
TRY_RESULT_PROMISE(promise, state, std::move(r_state));
|
||||
|
|
|
@ -91,6 +91,7 @@ void Query::start_up() {
|
|||
|
||||
void Query::handle_error(td::Status error) {
|
||||
td::TerminalIO::out() << "Failed " << name() << " query: " << error << "\n";
|
||||
td::actor::send_closure(console_, &ValidatorEngineConsole::got_result, false);
|
||||
stop();
|
||||
}
|
||||
|
||||
|
@ -99,7 +100,7 @@ void Query::receive_wrap(td::BufferSlice R) {
|
|||
if (S.is_error()) {
|
||||
handle_error(std::move(S));
|
||||
} else {
|
||||
td::actor::send_closure(console_, &ValidatorEngineConsole::got_result);
|
||||
td::actor::send_closure(console_, &ValidatorEngineConsole::got_result, true);
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
@ -1004,3 +1005,53 @@ td::Status ImportShardOverlayCertificateQuery::receive(td::BufferSlice data) {
|
|||
td::TerminalIO::out() << "successfully sent certificate to overlay manager\n";
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -1075,3 +1075,24 @@ class ImportShardOverlayCertificateQuery : public Query {
|
|||
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_;
|
||||
};
|
||||
|
|
|
@ -140,6 +140,7 @@ void ValidatorEngineConsole::run() {
|
|||
add_query_runner(std::make_unique<QueryRunnerImpl<GetOverlaysStatsJsonQuery>>());
|
||||
add_query_runner(std::make_unique<QueryRunnerImpl<ImportShardOverlayCertificateQuery>>());
|
||||
add_query_runner(std::make_unique<QueryRunnerImpl<SignShardOverlayCertificateQuery>>());
|
||||
add_query_runner(std::make_unique<QueryRunnerImpl<GetPerfTimerStatsJsonQuery>>());
|
||||
}
|
||||
|
||||
bool ValidatorEngineConsole::envelope_send_query(td::BufferSlice query, td::Promise<td::BufferSlice> promise) {
|
||||
|
@ -168,7 +169,10 @@ bool ValidatorEngineConsole::envelope_send_query(td::BufferSlice query, td::Prom
|
|||
return true;
|
||||
}
|
||||
|
||||
void ValidatorEngineConsole::got_result() {
|
||||
void ValidatorEngineConsole::got_result(bool success) {
|
||||
if (!success && ex_mode_) {
|
||||
std::_Exit(2);
|
||||
}
|
||||
running_queries_--;
|
||||
if (!running_queries_ && ex_queries_.size() > 0) {
|
||||
auto data = std::move(ex_queries_[0]);
|
||||
|
|
|
@ -112,7 +112,7 @@ class ValidatorEngineConsole : public td::actor::Actor {
|
|||
}
|
||||
|
||||
bool envelope_send_query(td::BufferSlice query, td::Promise<td::BufferSlice> promise);
|
||||
void got_result();
|
||||
void got_result(bool success = true);
|
||||
void show_help(std::string command, td::Promise<td::BufferSlice> promise);
|
||||
void show_license(td::Promise<td::BufferSlice> promise);
|
||||
|
||||
|
|
|
@ -66,9 +66,11 @@
|
|||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <set>
|
||||
#include "git.h"
|
||||
|
||||
|
||||
Config::Config() {
|
||||
out_port = 3278;
|
||||
full_node = ton::PublicKeyHash::zero();
|
||||
|
@ -1338,6 +1340,9 @@ td::Status ValidatorEngine::load_global_config() {
|
|||
if (truncate_seqno_ > 0) {
|
||||
validator_options_.write().truncate_db(truncate_seqno_);
|
||||
}
|
||||
if (!session_logs_file_.empty()) {
|
||||
validator_options_.write().set_session_logs_file(session_logs_file_);
|
||||
}
|
||||
|
||||
std::vector<ton::BlockIdExt> h;
|
||||
for (auto &x : conf.validator_->hardforks_) {
|
||||
|
@ -3277,6 +3282,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::process_control_query(td::uint16 port, ton::adnl::AdnlNodeIdShort src,
|
||||
ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data,
|
||||
td::Promise<td::BufferSlice> promise) {
|
||||
|
@ -3466,7 +3522,11 @@ int main(int argc, char *argv[]) {
|
|||
#endif
|
||||
td::set_signal_handler(td::SignalType::HangUp, force_rotate_logs).ensure();
|
||||
});
|
||||
std::string session_logs_file;
|
||||
p.add_option('l', "logname", "log to file", [&](td::Slice fname) {
|
||||
if (session_logs_file.empty()) {
|
||||
session_logs_file = fname.str() + ".session-stats";
|
||||
}
|
||||
logger_ = td::TsFileLog::create(fname.str()).move_as_ok();
|
||||
td::log_interface = logger_.get();
|
||||
});
|
||||
|
@ -3503,6 +3563,9 @@ int main(int argc, char *argv[]) {
|
|||
auto v = td::to_integer<ton::BlockSeqno>(fname);
|
||||
acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_truncate_seqno, v); });
|
||||
});
|
||||
p.add_option('\0', "session-logs", "file for validator session stats (default: {logname}.session-stats)",
|
||||
[&](td::Slice fname) { session_logs_file = fname.str(); });
|
||||
acts.push_back([&]() { td::actor::send_closure(x, &ValidatorEngine::set_session_logs_file, session_logs_file); });
|
||||
p.add_checked_option(
|
||||
'U', "unsafe-catchain-restore", "use SLOW and DANGEROUS catchain recover method", [&](td::Slice id) {
|
||||
TRY_RESULT(seq, td::to_integer_safe<ton::CatchainSeqno>(id));
|
||||
|
|
|
@ -201,6 +201,7 @@ class ValidatorEngine : public td::actor::Actor {
|
|||
bool started_keyring_ = false;
|
||||
bool started_ = false;
|
||||
ton::BlockSeqno truncate_seqno_{0};
|
||||
std::string session_logs_file_;
|
||||
|
||||
std::set<ton::CatchainSeqno> unsafe_catchains_;
|
||||
std::map<ton::BlockSeqno, std::pair<ton::CatchainSeqno, td::uint32>> unsafe_catchain_rotations_;
|
||||
|
@ -243,6 +244,9 @@ class ValidatorEngine : public td::actor::Actor {
|
|||
void set_truncate_seqno(ton::BlockSeqno seqno) {
|
||||
truncate_seqno_ = seqno;
|
||||
}
|
||||
void set_session_logs_file(std::string f) {
|
||||
session_logs_file_ = std::move(f);
|
||||
}
|
||||
void add_ip(td::IPAddress addr) {
|
||||
addrs_.push_back(addr);
|
||||
}
|
||||
|
@ -250,7 +254,6 @@ class ValidatorEngine : public td::actor::Actor {
|
|||
keys_[key.compute_short_id()] = key;
|
||||
}
|
||||
void start_up() override;
|
||||
void got_result();
|
||||
ValidatorEngine() {
|
||||
}
|
||||
|
||||
|
@ -404,6 +407,8 @@ class ValidatorEngine : public td::actor::Actor {
|
|||
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise);
|
||||
void run_control_query(ton::ton_api::engine_validator_getOverlaysStats &query, td::BufferSlice data,
|
||||
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>
|
||||
void run_control_query(T &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm,
|
||||
td::Promise<td::BufferSlice> promise) {
|
||||
|
|
|
@ -162,20 +162,28 @@ void ValidatorSessionDescriptionImpl::update_hash(const RootObject *obj, HashTyp
|
|||
}
|
||||
|
||||
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) {
|
||||
pdata_temp_ptr_ += get_padding(pdata_temp_ + pdata_temp_ptr_);
|
||||
auto s = pdata_temp_ptr_;
|
||||
pdata_temp_ptr_ += size;
|
||||
CHECK(s + size <= pdata_temp_size_);
|
||||
return static_cast<void *>(pdata_temp_ + s);
|
||||
} else {
|
||||
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;
|
||||
|
||||
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_]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,6 +63,33 @@ struct ValidatorSessionNode {
|
|||
ValidatorWeight weight;
|
||||
};
|
||||
|
||||
struct ValidatorSessionStats {
|
||||
enum { status_none = 0, status_received = 1, status_rejected = 2, status_approved = 3 };
|
||||
|
||||
struct Producer {
|
||||
PublicKeyHash id = PublicKeyHash::zero();
|
||||
int block_status = status_none;
|
||||
td::uint64 block_timestamp = 0;
|
||||
};
|
||||
struct Round {
|
||||
td::uint64 timestamp = 0;
|
||||
std::vector<Producer> producers;
|
||||
};
|
||||
|
||||
td::uint32 first_round;
|
||||
std::vector<Round> rounds;
|
||||
|
||||
td::uint64 timestamp = 0;
|
||||
PublicKeyHash self = PublicKeyHash::zero();
|
||||
PublicKeyHash creator = PublicKeyHash::zero();
|
||||
td::uint32 total_validators = 0;
|
||||
ValidatorWeight total_weight = 0;
|
||||
td::uint32 signatures = 0;
|
||||
ValidatorWeight signatures_weight = 0;
|
||||
td::uint32 approve_signatures = 0;
|
||||
ValidatorWeight approve_signatures_weight = 0;
|
||||
};
|
||||
|
||||
} // namespace validatorsession
|
||||
|
||||
} // namespace ton
|
||||
|
|
|
@ -260,6 +260,7 @@ void ValidatorSessionImpl::process_broadcast(PublicKeyHash src, td::BufferSlice
|
|||
CHECK(!pending_reject_.count(block_id));
|
||||
CHECK(!rejected_.count(block_id));
|
||||
|
||||
stats_set_candidate_status(cur_round_, src, ValidatorSessionStats::status_received);
|
||||
auto v = virtual_state_->choose_blocks_to_approve(description(), local_idx());
|
||||
for (auto &b : v) {
|
||||
if (b && SentBlock::get_block_id(b) == block_id) {
|
||||
|
@ -331,7 +332,8 @@ void ValidatorSessionImpl::process_query(PublicKeyHash src, td::BufferSlice data
|
|||
}
|
||||
|
||||
void ValidatorSessionImpl::candidate_decision_fail(td::uint32 round, ValidatorSessionCandidateId hash,
|
||||
std::string result, td::BufferSlice proof) {
|
||||
std::string result, td::uint32 src, td::BufferSlice proof) {
|
||||
stats_set_candidate_status(round, description().get_source_id(src), ValidatorSessionStats::status_rejected);
|
||||
if (round != cur_round_) {
|
||||
return;
|
||||
}
|
||||
|
@ -345,7 +347,8 @@ void ValidatorSessionImpl::candidate_decision_fail(td::uint32 round, ValidatorSe
|
|||
}
|
||||
|
||||
void ValidatorSessionImpl::candidate_decision_ok(td::uint32 round, ValidatorSessionCandidateId hash, RootHash root_hash,
|
||||
FileHash file_hash, td::uint32 ok_from) {
|
||||
FileHash file_hash, td::uint32 src, td::uint32 ok_from) {
|
||||
stats_set_candidate_status(round, description().get_source_id(src), ValidatorSessionStats::status_approved);
|
||||
if (round != cur_round_) {
|
||||
return;
|
||||
}
|
||||
|
@ -512,6 +515,7 @@ void ValidatorSessionImpl::try_approve_block(const SentBlock *block) {
|
|||
|
||||
auto P = td::PromiseCreator::lambda([round = cur_round_, hash = block_id, root_hash = block->get_root_hash(),
|
||||
file_hash = block->get_file_hash(), timer = std::move(timer),
|
||||
src = block->get_src_idx(),
|
||||
SelfId = actor_id(this)](td::Result<CandidateDecision> res) {
|
||||
if (res.is_error()) {
|
||||
LOG(ERROR) << "round " << round << " failed to validate candidate " << hash << ": " << res.move_as_error();
|
||||
|
@ -521,10 +525,10 @@ void ValidatorSessionImpl::try_approve_block(const SentBlock *block) {
|
|||
auto R = res.move_as_ok();
|
||||
if (R.is_ok()) {
|
||||
td::actor::send_closure(SelfId, &ValidatorSessionImpl::candidate_decision_ok, round, hash, root_hash,
|
||||
file_hash, R.ok_from());
|
||||
file_hash, src, R.ok_from());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ValidatorSessionImpl::candidate_decision_fail, round, hash, R.reason(),
|
||||
R.proof());
|
||||
src, R.proof());
|
||||
}
|
||||
});
|
||||
pending_approve_.insert(block_id);
|
||||
|
@ -746,38 +750,60 @@ void ValidatorSessionImpl::on_new_round(td::uint32 round) {
|
|||
CHECK(approve_sigs);
|
||||
|
||||
std::vector<std::pair<PublicKeyHash, td::BufferSlice>> export_sigs;
|
||||
ValidatorWeight signatures_weight = 0;
|
||||
CHECK(sigs->size() == description().get_total_nodes());
|
||||
for (td::uint32 i = 0; i < description().get_total_nodes(); i++) {
|
||||
auto sig = sigs->at(i);
|
||||
if (sig) {
|
||||
CHECK(description().is_persistent(sig));
|
||||
export_sigs.emplace_back(description().get_source_id(i), sig->value().clone());
|
||||
signatures_weight += description().get_node_weight(i);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<PublicKeyHash, td::BufferSlice>> export_approve_sigs;
|
||||
ValidatorWeight approve_signatures_weight = 0;
|
||||
CHECK(approve_sigs->size() == description().get_total_nodes());
|
||||
for (td::uint32 i = 0; i < description().get_total_nodes(); i++) {
|
||||
auto sig = approve_sigs->at(i);
|
||||
if (sig) {
|
||||
CHECK(description().is_persistent(sig));
|
||||
export_approve_sigs.emplace_back(description().get_source_id(i), sig->value().clone());
|
||||
approve_signatures_weight += description().get_node_weight(i);
|
||||
}
|
||||
}
|
||||
|
||||
auto it = blocks_[0].find(SentBlock::get_block_id(block));
|
||||
if (!block) {
|
||||
bool have_block = (bool)block;
|
||||
if (!have_block) {
|
||||
callback_->on_block_skipped(cur_round_);
|
||||
} else if (it == blocks_[0].end()) {
|
||||
} else {
|
||||
cur_stats_.timestamp = (td::uint64)td::Clocks::system();
|
||||
cur_stats_.total_validators = description().get_total_nodes();
|
||||
cur_stats_.total_weight = description().get_total_weight();
|
||||
cur_stats_.signatures = (td::uint32)export_sigs.size();
|
||||
cur_stats_.signatures_weight = signatures_weight;
|
||||
cur_stats_.approve_signatures = (td::uint32)export_approve_sigs.size();
|
||||
cur_stats_.approve_signatures_weight = approve_signatures_weight;
|
||||
cur_stats_.creator = description().get_source_id(block->get_src_idx());
|
||||
cur_stats_.self = description().get_source_id(local_idx());
|
||||
|
||||
if (it == blocks_[0].end()) {
|
||||
callback_->on_block_committed(cur_round_, description().get_source_public_key(block->get_src_idx()),
|
||||
block->get_root_hash(), block->get_file_hash(), td::BufferSlice(),
|
||||
std::move(export_sigs), std::move(export_approve_sigs));
|
||||
std::move(export_sigs), std::move(export_approve_sigs), std::move(cur_stats_));
|
||||
} else {
|
||||
callback_->on_block_committed(cur_round_, description().get_source_public_key(block->get_src_idx()),
|
||||
block->get_root_hash(), block->get_file_hash(), it->second->data_.clone(),
|
||||
std::move(export_sigs), std::move(export_approve_sigs));
|
||||
std::move(export_sigs), std::move(export_approve_sigs), std::move(cur_stats_));
|
||||
}
|
||||
}
|
||||
cur_round_++;
|
||||
if (have_block) {
|
||||
stats_init();
|
||||
} else {
|
||||
stats_add_round();
|
||||
}
|
||||
for (size_t i = 0; i < blocks_.size() - 1; i++) {
|
||||
blocks_[i] = std::move(blocks_[i + 1]);
|
||||
}
|
||||
|
@ -868,6 +894,47 @@ void ValidatorSessionImpl::start_up() {
|
|||
|
||||
check_all();
|
||||
td::actor::send_closure(rldp_, &rldp::Rldp::add_id, description().get_source_adnl_id(local_idx()));
|
||||
|
||||
stats_init();
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::stats_init() {
|
||||
cur_stats_ = ValidatorSessionStats();
|
||||
cur_stats_.first_round = cur_round_;
|
||||
stats_add_round();
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::stats_add_round() {
|
||||
cur_stats_.rounds.emplace_back();
|
||||
auto& round = cur_stats_.rounds.back();
|
||||
round.timestamp = (td::uint64)td::Clocks::system();
|
||||
round.producers.resize(description().get_max_priority() + 1);
|
||||
for (td::uint32 i = 0; i < description().get_total_nodes(); i++) {
|
||||
td::int32 priority = description().get_node_priority(i, cur_round_);
|
||||
if (priority >= 0) {
|
||||
CHECK((size_t)priority < round.producers.size());
|
||||
round.producers[priority].id = description().get_source_id(i);
|
||||
}
|
||||
}
|
||||
while (!round.producers.empty() && round.producers.back().id.is_zero()) {
|
||||
round.producers.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void ValidatorSessionImpl::stats_set_candidate_status(td::uint32 round, PublicKeyHash src, int status) {
|
||||
if (round < cur_stats_.first_round || round - cur_stats_.first_round >= cur_stats_.rounds.size()) {
|
||||
return;
|
||||
}
|
||||
auto& stats_round = cur_stats_.rounds[round - cur_stats_.first_round];
|
||||
auto it = std::find_if(stats_round.producers.begin(), stats_round.producers.end(),
|
||||
[&](const ValidatorSessionStats::Producer& p) { return p.id == src; });
|
||||
if (it == stats_round.producers.end()) {
|
||||
return;
|
||||
}
|
||||
if (it->block_status == ValidatorSessionStats::status_none) {
|
||||
it->block_timestamp = (td::uint64)td::Clocks::system();
|
||||
}
|
||||
it->block_status = status;
|
||||
}
|
||||
|
||||
td::actor::ActorOwn<ValidatorSession> ValidatorSession::create(
|
||||
|
|
|
@ -79,7 +79,8 @@ class ValidatorSession : public td::actor::Actor {
|
|||
virtual void on_block_committed(td::uint32 round, PublicKey source, ValidatorSessionRootHash root_hash,
|
||||
ValidatorSessionFileHash file_hash, td::BufferSlice data,
|
||||
std::vector<std::pair<PublicKeyHash, td::BufferSlice>> signatures,
|
||||
std::vector<std::pair<PublicKeyHash, td::BufferSlice>> approve_signatures) = 0;
|
||||
std::vector<std::pair<PublicKeyHash, td::BufferSlice>> approve_signatures,
|
||||
ValidatorSessionStats stats) = 0;
|
||||
virtual void on_block_skipped(td::uint32 round) = 0;
|
||||
virtual void get_approved_candidate(PublicKey source, ValidatorSessionRootHash root_hash,
|
||||
ValidatorSessionFileHash file_hash,
|
||||
|
|
|
@ -154,6 +154,11 @@ class ValidatorSessionImpl : public ValidatorSession {
|
|||
bool catchain_started_ = false;
|
||||
bool allow_unsafe_self_blocks_resync_;
|
||||
|
||||
ValidatorSessionStats cur_stats_;
|
||||
void stats_init();
|
||||
void stats_add_round();
|
||||
void stats_set_candidate_status(td::uint32 round, PublicKeyHash src, int status);
|
||||
|
||||
public:
|
||||
ValidatorSessionImpl(catchain::CatChainSessionId session_id, ValidatorSessionOptions opts, PublicKeyHash local_id,
|
||||
std::vector<ValidatorSessionNode> nodes, std::unique_ptr<Callback> callback,
|
||||
|
@ -177,9 +182,9 @@ class ValidatorSessionImpl : public ValidatorSession {
|
|||
void try_sign();
|
||||
|
||||
void candidate_decision_fail(td::uint32 round, ValidatorSessionCandidateId hash, std::string result,
|
||||
td::BufferSlice proof);
|
||||
td::uint32 src, td::BufferSlice proof);
|
||||
void candidate_decision_ok(td::uint32 round, ValidatorSessionCandidateId hash, RootHash root_hash, FileHash file_hash,
|
||||
td::uint32 ok_from);
|
||||
td::uint32 src, td::uint32 ok_from);
|
||||
void candidate_approved_signed(td::uint32 round, ValidatorSessionCandidateId hash, td::uint32 ok_from,
|
||||
td::BufferSlice signature);
|
||||
|
||||
|
|
|
@ -46,7 +46,10 @@ class ApplyBlock : public td::actor::Actor {
|
|||
, masterchain_block_id_(masterchain_block_id)
|
||||
, manager_(manager)
|
||||
, 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() {
|
||||
|
@ -78,7 +81,7 @@ class ApplyBlock : public td::actor::Actor {
|
|||
BlockHandle handle_;
|
||||
td::Ref<ShardState> state_;
|
||||
|
||||
td::PerfWarningTimer perf_timer_{"applyblock", 0.1};
|
||||
td::PerfWarningTimer perf_timer_;
|
||||
};
|
||||
|
||||
} // namespace validator
|
||||
|
|
|
@ -35,7 +35,10 @@ class WaitBlockData : public td::actor::Actor {
|
|||
, priority_(priority)
|
||||
, manager_(manager)
|
||||
, 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) {
|
||||
|
@ -74,7 +77,7 @@ class WaitBlockData : public td::actor::Actor {
|
|||
bool is_hardfork_ = false;
|
||||
td::Timestamp try_read_static_file_ = td::Timestamp::now();
|
||||
|
||||
//td::PerfWarningTimer perf_timer_{"waitdata", 1.0};
|
||||
td::PerfWarningTimer perf_timer_;
|
||||
};
|
||||
|
||||
} // namespace validator
|
||||
|
|
|
@ -32,7 +32,10 @@ class WaitBlockState : public td::actor::Actor {
|
|||
, priority_(priority)
|
||||
, manager_(manager)
|
||||
, timeout_(timeout)
|
||||
, promise_(std::move(promise)) {
|
||||
, promise_(std::move(promise))
|
||||
, perf_timer_("waitstate", 1.0, [manager](double duration) {
|
||||
send_closure(manager, &ValidatorManager::add_perf_timer_stat, "waitstate", duration);
|
||||
}) {
|
||||
}
|
||||
|
||||
void abort_query(td::Status reason);
|
||||
|
@ -80,7 +83,7 @@ class WaitBlockState : public td::actor::Actor {
|
|||
bool reading_from_db_ = false;
|
||||
td::Timestamp next_static_file_attempt_;
|
||||
|
||||
//td::PerfWarningTimer perf_timer_{"waitstate", 1.0};
|
||||
td::PerfWarningTimer perf_timer_;
|
||||
};
|
||||
|
||||
} // namespace validator
|
||||
|
|
|
@ -53,7 +53,10 @@ AcceptBlockQuery::AcceptBlockQuery(BlockIdExt id, td::Ref<BlockData> data, std::
|
|||
, is_fork_(false)
|
||||
, send_broadcast_(send_broadcast)
|
||||
, 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_old_hash_.clear();
|
||||
state_hash_.clear();
|
||||
|
@ -71,7 +74,10 @@ AcceptBlockQuery::AcceptBlockQuery(AcceptBlockQuery::IsFake fake, BlockIdExt id,
|
|||
, is_fork_(false)
|
||||
, send_broadcast_(false)
|
||||
, 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_old_hash_.clear();
|
||||
state_hash_.clear();
|
||||
|
@ -86,7 +92,10 @@ AcceptBlockQuery::AcceptBlockQuery(ForceFork ffork, BlockIdExt id, td::Ref<Block
|
|||
, is_fork_(true)
|
||||
, send_broadcast_(false)
|
||||
, 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_old_hash_.clear();
|
||||
state_hash_.clear();
|
||||
|
|
|
@ -126,7 +126,7 @@ class AcceptBlockQuery : public td::actor::Actor {
|
|||
td::BufferSlice top_block_descr_data_;
|
||||
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);
|
||||
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)
|
||||
, timeout_(timeout)
|
||||
, 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,
|
||||
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)
|
||||
, promise_(std::move(promise))
|
||||
, 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,
|
||||
td::Timestamp timeout, td::Promise<BlockHandle> promise)
|
||||
|
@ -67,7 +73,10 @@ class CheckProof : public td::actor::Actor {
|
|||
, proof_(std::move(proof_link))
|
||||
, manager_(manager)
|
||||
, 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:
|
||||
|
@ -114,7 +123,7 @@ class CheckProof : public td::actor::Actor {
|
|||
bool skip_check_signatures_{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);
|
||||
template <typename T>
|
||||
|
|
|
@ -208,7 +208,7 @@ class Collator final : public td::actor::Actor {
|
|||
std::vector<Ref<vm::Cell>> collated_roots_;
|
||||
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;
|
||||
std::unique_ptr<block::Account> make_account_from(td::ConstBitPtr addr, Ref<vm::CellSlice> account,
|
||||
|
|
|
@ -67,7 +67,10 @@ Collator::Collator(ShardIdFull shard, bool is_hardfork, UnixTime min_ts, BlockId
|
|||
, validator_set_(std::move(validator_set))
|
||||
, manager(manager)
|
||||
, 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() {
|
||||
|
|
|
@ -201,6 +201,9 @@ void LiteQuery::start_up() {
|
|||
[&](lite_api::liteServer_getLibraries& q) {
|
||||
this->perform_getLibraries(q.library_list_);
|
||||
},
|
||||
[&](lite_api::liteServer_getShardBlockProof& q) {
|
||||
this->perform_getShardBlockProof(create_block_id(q.id_));
|
||||
},
|
||||
[&](auto& obj) { this->abort_query(td::Status::Error(ErrorCode::protoviolation, "unknown query")); }));
|
||||
}
|
||||
|
||||
|
@ -269,8 +272,13 @@ void LiteQuery::perform_getBlock(BlockIdExt blkid) {
|
|||
fatal_error("invalid BlockIdExt");
|
||||
return;
|
||||
}
|
||||
td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid,
|
||||
[Self = actor_id(this), blkid](td::Result<Ref<ton::validator::BlockData>> res) {
|
||||
get_block_handle_checked(blkid, [manager = manager_, Self = actor_id(this), blkid](td::Result<ConstBlockHandle> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error());
|
||||
return;
|
||||
}
|
||||
td::actor::send_closure_later(manager, &ValidatorManager::get_block_data_from_db, R.move_as_ok(),
|
||||
[=](td::Result<Ref<ton::validator::BlockData>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
|
||||
} else {
|
||||
|
@ -278,6 +286,7 @@ void LiteQuery::perform_getBlock(BlockIdExt blkid) {
|
|||
res.move_as_ok());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void LiteQuery::continue_getBlock(BlockIdExt blkid, Ref<ton::validator::BlockData> block) {
|
||||
|
@ -295,8 +304,13 @@ void LiteQuery::perform_getBlockHeader(BlockIdExt blkid, int mode) {
|
|||
fatal_error("invalid BlockIdExt");
|
||||
return;
|
||||
}
|
||||
td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid,
|
||||
[Self = actor_id(this), blkid, mode](td::Result<Ref<ton::validator::BlockData>> res) {
|
||||
get_block_handle_checked(blkid, [=, manager = manager_, Self = actor_id(this)](td::Result<ConstBlockHandle> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error());
|
||||
return;
|
||||
}
|
||||
td::actor::send_closure_later(manager, &ValidatorManager::get_block_data_from_db, R.move_as_ok(),
|
||||
[=](td::Result<Ref<ton::validator::BlockData>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
|
||||
} else {
|
||||
|
@ -304,6 +318,7 @@ void LiteQuery::perform_getBlockHeader(BlockIdExt blkid, int mode) {
|
|||
mode, res.move_as_ok());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static bool visit(Ref<vm::Cell> cell);
|
||||
|
@ -409,9 +424,14 @@ void LiteQuery::perform_getState(BlockIdExt blkid) {
|
|||
fatal_error("cannot request total state: possibly too large");
|
||||
return;
|
||||
}
|
||||
get_block_handle_checked(blkid, [=, manager = manager_, Self = actor_id(this)](td::Result<ConstBlockHandle> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error());
|
||||
return;
|
||||
}
|
||||
if (blkid.id.seqno) {
|
||||
td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, blkid,
|
||||
[Self = actor_id(this), blkid](td::Result<Ref<ton::validator::ShardState>> res) {
|
||||
td::actor::send_closure_later(manager, &ValidatorManager::get_shard_state_from_db, R.move_as_ok(),
|
||||
[=](td::Result<Ref<ton::validator::ShardState>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
|
||||
} else {
|
||||
|
@ -420,8 +440,8 @@ void LiteQuery::perform_getState(BlockIdExt blkid) {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
td::actor::send_closure_later(manager_, &ValidatorManager::get_zero_state, blkid,
|
||||
[Self = actor_id(this), blkid](td::Result<td::BufferSlice> res) {
|
||||
td::actor::send_closure_later(manager, &ValidatorManager::get_zero_state, blkid,
|
||||
[=](td::Result<td::BufferSlice> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
|
||||
} else {
|
||||
|
@ -430,6 +450,7 @@ void LiteQuery::perform_getState(BlockIdExt blkid) {
|
|||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void LiteQuery::continue_getState(BlockIdExt blkid, Ref<ton::validator::ShardState> state) {
|
||||
|
@ -481,6 +502,23 @@ void LiteQuery::perform_sendMessage(td::BufferSlice data) {
|
|||
});
|
||||
}
|
||||
|
||||
void LiteQuery::get_block_handle_checked(BlockIdExt blkid, td::Promise<ConstBlockHandle> promise) {
|
||||
auto P = td::PromiseCreator::lambda(
|
||||
[promise = std::move(promise)](td::Result<BlockHandle> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
} else {
|
||||
auto handle = R.move_as_ok();
|
||||
if (handle->is_applied()) {
|
||||
promise.set_result(std::move(handle));
|
||||
} else {
|
||||
promise.set_error(td::Status::Error("block is not applied"));
|
||||
}
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(manager_, &ValidatorManager::get_block_handle, blkid, false, std::move(P));
|
||||
}
|
||||
|
||||
bool LiteQuery::request_mc_block_data(BlockIdExt blkid) {
|
||||
if (!blkid.is_masterchain() || !blkid.is_valid_full()) {
|
||||
return fatal_error("reference block must belong to the masterchain");
|
||||
|
@ -579,9 +617,14 @@ bool LiteQuery::request_block_state(BlockIdExt blkid) {
|
|||
}
|
||||
blk_id_ = blkid;
|
||||
++pending_;
|
||||
get_block_handle_checked(blkid, [=, manager = manager_, Self = actor_id(this)](td::Result<ConstBlockHandle> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error());
|
||||
return;
|
||||
}
|
||||
td::actor::send_closure_later(
|
||||
manager_, &ValidatorManager::get_shard_state_from_db_short, blkid,
|
||||
[Self = actor_id(this), blkid](td::Result<Ref<ShardState>> res) {
|
||||
manager, &ValidatorManager::get_shard_state_from_db, R.move_as_ok(),
|
||||
[=](td::Result<Ref<ShardState>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query,
|
||||
res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : "));
|
||||
|
@ -589,6 +632,7 @@ bool LiteQuery::request_block_state(BlockIdExt blkid) {
|
|||
td::actor::send_closure_later(Self, &LiteQuery::got_block_state, blkid, res.move_as_ok());
|
||||
}
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -601,9 +645,14 @@ bool LiteQuery::request_block_data(BlockIdExt blkid) {
|
|||
}
|
||||
blk_id_ = blkid;
|
||||
++pending_;
|
||||
get_block_handle_checked(blkid, [=, manager = manager_, Self = actor_id(this)](td::Result<ConstBlockHandle> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error());
|
||||
return;
|
||||
}
|
||||
td::actor::send_closure_later(
|
||||
manager_, &ValidatorManager::get_block_data_from_db_short, blkid,
|
||||
[Self = actor_id(this), blkid](td::Result<Ref<BlockData>> res) {
|
||||
manager, &ValidatorManager::get_block_data_from_db, R.move_as_ok(),
|
||||
[=](td::Result<Ref<BlockData>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query,
|
||||
res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : "));
|
||||
|
@ -611,6 +660,7 @@ bool LiteQuery::request_block_data(BlockIdExt blkid) {
|
|||
td::actor::send_closure_later(Self, &LiteQuery::got_block_data, blkid, res.move_as_ok());
|
||||
}
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -646,16 +696,23 @@ bool LiteQuery::request_proof_link(BlockIdExt blkid) {
|
|||
});
|
||||
});
|
||||
} else {
|
||||
get_block_handle_checked(blkid, [=, manager = manager_, Self = actor_id(this)](td::Result<ConstBlockHandle> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error());
|
||||
return;
|
||||
}
|
||||
td::actor::send_closure_later(
|
||||
manager_, &ValidatorManager::get_block_proof_link_from_db_short, blkid,
|
||||
[Self = actor_id(this), blkid](td::Result<Ref<ProofLink>> res) {
|
||||
manager, &ValidatorManager::get_block_proof_link_from_db, R.move_as_ok(),
|
||||
[=](td::Result<Ref<ProofLink>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query,
|
||||
td::actor::send_closure(
|
||||
Self, &LiteQuery::abort_query,
|
||||
res.move_as_error_prefix("cannot load proof link for "s + blkid.to_str() + " : "));
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::got_block_proof_link, blkid, res.move_as_ok());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -672,9 +729,14 @@ bool LiteQuery::request_zero_state(BlockIdExt blkid) {
|
|||
}
|
||||
blk_id_ = blkid;
|
||||
++pending_;
|
||||
get_block_handle_checked(blkid, [=, manager = manager_, Self = actor_id(this)](td::Result<ConstBlockHandle> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error());
|
||||
return;
|
||||
}
|
||||
td::actor::send_closure_later(
|
||||
manager_, &ValidatorManager::get_zero_state, blkid,
|
||||
[Self = actor_id(this), blkid](td::Result<td::BufferSlice> res) {
|
||||
manager, &ValidatorManager::get_zero_state, blkid,
|
||||
[=](td::Result<td::BufferSlice> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query,
|
||||
res.move_as_error_prefix("cannot load zerostate of "s + blkid.to_str() + " : "));
|
||||
|
@ -682,6 +744,7 @@ bool LiteQuery::request_zero_state(BlockIdExt blkid) {
|
|||
td::actor::send_closure_later(Self, &LiteQuery::got_zero_state, blkid, res.move_as_ok());
|
||||
}
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1397,6 +1460,11 @@ void LiteQuery::continue_getTransactions(unsigned remaining, bool exact) {
|
|||
td::actor::send_closure(Self, &LiteQuery::abort_getTransactions, res.move_as_error(), ton::BlockIdExt{});
|
||||
} else {
|
||||
auto handle = res.move_as_ok();
|
||||
if (!handle->is_applied()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_getTransactions, td::Status::Error("block is not applied"),
|
||||
ton::BlockIdExt{});
|
||||
return;
|
||||
}
|
||||
LOG(DEBUG) << "requesting data for block " << handle->id().to_str();
|
||||
td::actor::send_closure_later(manager, &ValidatorManager::get_block_data_from_db, handle,
|
||||
[Self, blkid = handle->id(), remaining](td::Result<Ref<BlockData>> res) {
|
||||
|
@ -1722,6 +1790,10 @@ void LiteQuery::perform_lookupBlock(BlockId blkid, int mode, LogicalTime lt, Uni
|
|||
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
|
||||
} else {
|
||||
auto handle = res.move_as_ok();
|
||||
if (!handle->is_applied()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, td::Status::Error("block is not applied"));
|
||||
return;
|
||||
}
|
||||
LOG(DEBUG) << "requesting data for block " << handle->id().to_str();
|
||||
td::actor::send_closure_later(manager, &ValidatorManager::get_block_data_from_db, handle,
|
||||
[Self, blkid = handle->id(), mode](td::Result<Ref<BlockData>> res) {
|
||||
|
@ -2363,5 +2435,135 @@ void LiteQuery::continue_getValidatorStats(int mode, int limit, Bits256 start_af
|
|||
finish_query(std::move(b));
|
||||
}
|
||||
|
||||
void LiteQuery::perform_getShardBlockProof(BlockIdExt blkid) {
|
||||
LOG(INFO) << "started a getMasterchainInfo(" << blkid.to_str() << ") liteserver query";
|
||||
if (!blkid.is_valid_ext()) {
|
||||
fatal_error("invalid block id");
|
||||
return;
|
||||
}
|
||||
if (blkid.is_masterchain()) {
|
||||
LOG(INFO) << "getShardBlockProof() query completed";
|
||||
auto b = create_serialize_tl_object<lite_api::liteServer_shardBlockProof>(
|
||||
create_tl_lite_block_id(blkid), std::vector<tl_object_ptr<lite_api::liteServer_shardBlockLink>>());
|
||||
finish_query(std::move(b));
|
||||
return;
|
||||
}
|
||||
blk_id_ = blkid;
|
||||
get_block_handle_checked(blkid, [manager = manager_, Self = actor_id(this)](td::Result<ConstBlockHandle> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error());
|
||||
return;
|
||||
}
|
||||
ConstBlockHandle handle = R.move_as_ok();
|
||||
if (!handle->inited_masterchain_ref_block()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, td::Status::Error("block doesn't have masterchain ref"));
|
||||
return;
|
||||
}
|
||||
AccountIdPrefixFull pfx{masterchainId, shardIdAll};
|
||||
td::actor::send_closure_later(
|
||||
manager, &ValidatorManager::get_block_by_seqno_from_db, pfx, handle->masterchain_ref_block(),
|
||||
[Self, manager](td::Result<ConstBlockHandle> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error());
|
||||
} else {
|
||||
ConstBlockHandle handle = R.move_as_ok();
|
||||
td::actor::send_closure_later(
|
||||
manager, &ValidatorManager::get_block_data_from_db, handle, [Self](td::Result<Ref<BlockData>> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::continue_getShardBlockProof, R.move_as_ok(),
|
||||
std::vector<std::pair<BlockIdExt, td::BufferSlice>>());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void LiteQuery::continue_getShardBlockProof(Ref<BlockData> cur_block,
|
||||
std::vector<std::pair<BlockIdExt, td::BufferSlice>> result) {
|
||||
BlockIdExt cur_id = cur_block->block_id();
|
||||
BlockIdExt prev_id;
|
||||
vm::MerkleProofBuilder mpb{cur_block->root_cell()};
|
||||
if (cur_id.is_masterchain()) {
|
||||
base_blk_id_ = cur_id;
|
||||
block::gen::Block::Record blk;
|
||||
block::gen::BlockExtra::Record extra;
|
||||
block::gen::McBlockExtra::Record mc_extra;
|
||||
if (!tlb::unpack_cell(mpb.root(), blk) || !tlb::unpack_cell(blk.extra, extra) || !extra.custom->have_refs() ||
|
||||
!tlb::unpack_cell(extra.custom->prefetch_ref(), mc_extra)) {
|
||||
fatal_error("cannot unpack header of block "s + cur_id.to_str());
|
||||
return;
|
||||
}
|
||||
block::ShardConfig shards(mc_extra.shard_hashes->prefetch_ref());
|
||||
ShardIdFull shard_id = blk_id_.shard_full();
|
||||
shard_id.shard = (shard_id.shard & ~(1 << (63 - shard_id.pfx_len()))) | 1;
|
||||
Ref<block::McShardHash> shard_hash = shards.get_shard_hash(shard_id, false);
|
||||
if (shard_hash.is_null()) {
|
||||
fatal_error("shard not found");
|
||||
return;
|
||||
}
|
||||
prev_id = shard_hash->top_block_id();
|
||||
} else {
|
||||
std::vector<BlockIdExt> prev;
|
||||
BlockIdExt mc_blkid;
|
||||
bool after_split;
|
||||
td::Status S = block::unpack_block_prev_blk_try(mpb.root(), cur_id, prev, mc_blkid, after_split);
|
||||
if (S.is_error()) {
|
||||
fatal_error(std::move(S));
|
||||
return;
|
||||
}
|
||||
bool found = false;
|
||||
for (const BlockIdExt& id : prev) {
|
||||
if (shard_intersects(id.shard_full(), blk_id_.shard_full())) {
|
||||
found = true;
|
||||
prev_id = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
fatal_error("failed to find block chain");
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto proof = mpb.extract_proof_boc();
|
||||
if (proof.is_error()) {
|
||||
fatal_error(proof.move_as_error_prefix("cannot serialize Merkle proof : "));
|
||||
return;
|
||||
}
|
||||
result.emplace_back(prev_id, proof.move_as_ok());
|
||||
|
||||
if (prev_id == blk_id_) {
|
||||
CHECK(base_blk_id_.is_masterchain());
|
||||
std::vector<tl_object_ptr<lite_api::liteServer_shardBlockLink>> links;
|
||||
for (auto& p : result) {
|
||||
links.push_back(
|
||||
create_tl_object<lite_api::liteServer_shardBlockLink>(create_tl_lite_block_id(p.first), std::move(p.second)));
|
||||
}
|
||||
LOG(INFO) << "getShardBlockProof() query completed";
|
||||
auto b = create_serialize_tl_object<lite_api::liteServer_shardBlockProof>(create_tl_lite_block_id(base_blk_id_),
|
||||
std::move(links));
|
||||
finish_query(std::move(b));
|
||||
return;
|
||||
}
|
||||
if (result.size() == 8) {
|
||||
// Chains of shardblocks between masterchain blocks can't be longer than 8 (see collator.cpp:991)
|
||||
fatal_error("proof chain is too long");
|
||||
return;
|
||||
}
|
||||
|
||||
td::actor::send_closure_later(
|
||||
manager_, &ValidatorManager::get_block_data_from_db_short, prev_id,
|
||||
[Self = actor_id(this), result = std::move(result)](td::Result<Ref<BlockData>> R) mutable {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::continue_getShardBlockProof, R.move_as_ok(),
|
||||
std::move(result));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace validator
|
||||
} // namespace ton
|
||||
|
|
|
@ -145,6 +145,9 @@ class LiteQuery : public td::actor::Actor {
|
|||
bool construct_proof_link_back_cont(ton::BlockIdExt cur, ton::BlockIdExt next);
|
||||
bool adjust_last_proof_link(ton::BlockIdExt cur, Ref<vm::Cell> block_root);
|
||||
bool finish_proof_chain(ton::BlockIdExt id);
|
||||
void perform_getShardBlockProof(BlockIdExt blkid);
|
||||
void continue_getShardBlockProof(Ref<BlockData> cur_block,
|
||||
std::vector<std::pair<BlockIdExt, td::BufferSlice>> result);
|
||||
|
||||
void load_prevKeyBlock(ton::BlockIdExt blkid, td::Promise<std::pair<BlockIdExt, Ref<BlockQ>>>);
|
||||
void continue_loadPrevKeyBlock(ton::BlockIdExt blkid, td::Result<std::pair<Ref<MasterchainState>, BlockIdExt>> res,
|
||||
|
@ -152,6 +155,7 @@ class LiteQuery : public td::actor::Actor {
|
|||
void finish_loadPrevKeyBlock(ton::BlockIdExt blkid, td::Result<Ref<BlockData>> res,
|
||||
td::Promise<std::pair<BlockIdExt, Ref<BlockQ>>> promise);
|
||||
|
||||
void get_block_handle_checked(BlockIdExt blkid, td::Promise<ConstBlockHandle> promise);
|
||||
bool request_block_data(BlockIdExt blkid);
|
||||
bool request_block_state(BlockIdExt blkid);
|
||||
bool request_block_data_state(BlockIdExt blkid);
|
||||
|
|
|
@ -59,12 +59,15 @@ ValidateQuery::ValidateQuery(ShardIdFull shard, UnixTime min_ts, BlockIdExt min_
|
|||
, prev_blocks(std::move(prev))
|
||||
, block_candidate(std::move(candidate))
|
||||
, validator_set_(std::move(validator_set))
|
||||
, manager(std::move(manager))
|
||||
, manager(manager)
|
||||
, timeout(timeout)
|
||||
, main_promise(std::move(promise))
|
||||
, is_fake_(is_fake)
|
||||
, shard_pfx_(shard_.shard)
|
||||
, shard_pfx_len_(ton::shard_prefix_length(shard_)) {
|
||||
, shard_pfx_len_(ton::shard_prefix_length(shard_))
|
||||
, perf_timer_("validateblock", 0.1, [manager](double duration) {
|
||||
send_closure(manager, &ValidatorManager::add_perf_timer_stat, "validateblock", duration);
|
||||
}) {
|
||||
proc_hash_.zero();
|
||||
}
|
||||
|
||||
|
@ -2310,10 +2313,6 @@ bool ValidateQuery::precheck_one_account_update(td::ConstBitPtr acc_id, Ref<vm::
|
|||
"AccountBlock for this account");
|
||||
}
|
||||
if (new_value.not_null()) {
|
||||
if (!block::gen::t_ShardAccount.validate_csr(10000, new_value)) {
|
||||
return reject_query("new state of account "s + acc_id.to_hex(256) +
|
||||
" failed to pass automated validity checks for ShardAccount");
|
||||
}
|
||||
if (!block::tlb::t_ShardAccount.validate_csr(10000, new_value)) {
|
||||
return reject_query("new state of account "s + acc_id.to_hex(256) +
|
||||
" failed to pass hand-written validity checks for ShardAccount");
|
||||
|
|
|
@ -228,7 +228,7 @@ class ValidateQuery : public td::actor::Actor {
|
|||
|
||||
std::vector<std::tuple<Bits256, Bits256, bool>> lib_publishers_, lib_publishers2_;
|
||||
|
||||
td::PerfWarningTimer perf_timer_{"validateblock", 0.1};
|
||||
td::PerfWarningTimer perf_timer_;
|
||||
|
||||
static constexpr td::uint32 priority() {
|
||||
return 2;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "validator/validator.h"
|
||||
#include "liteserver.h"
|
||||
#include "crypto/vm/db/DynamicBagOfCellsDb.h"
|
||||
#include "validator-session/validator-session-types.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
|
@ -166,6 +167,8 @@ class ValidatorManager : public ValidatorManagerInterface {
|
|||
|
||||
virtual void wait_shard_client_state(BlockSeqno seqno, td::Timestamp timeout, td::Promise<td::Unit> promise) = 0;
|
||||
|
||||
virtual void log_validator_session_stats(BlockIdExt block_id, validatorsession::ValidatorSessionStats stats) = 0;
|
||||
|
||||
static bool is_persistent_state(UnixTime ts, UnixTime prev_ts) {
|
||||
return ts / (1 << 17) != prev_ts / (1 << 17);
|
||||
}
|
||||
|
|
|
@ -358,12 +358,22 @@ class ValidatorManagerImpl : public ValidatorManager {
|
|||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void prepare_perf_timer_stats(td::Promise<std::vector<PerfTimerStats>> promise) override {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void add_perf_timer_stat(std::string name, double duration) override {
|
||||
}
|
||||
|
||||
void truncate(BlockSeqno seqno, ConstBlockHandle handle, td::Promise<td::Unit> promise) override {
|
||||
UNREACHABLE();
|
||||
}
|
||||
void wait_shard_client_state(BlockSeqno seqno, td::Timestamp timeout, td::Promise<td::Unit> promise) override {
|
||||
UNREACHABLE();
|
||||
}
|
||||
void log_validator_session_stats(BlockIdExt block_id, validatorsession::ValidatorSessionStats stats) override {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
private:
|
||||
PublicKeyHash local_id_;
|
||||
|
|
|
@ -417,12 +417,22 @@ class ValidatorManagerImpl : public ValidatorManager {
|
|||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void prepare_perf_timer_stats(td::Promise<std::vector<PerfTimerStats>> promise) override {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void add_perf_timer_stat(std::string name, double duration) override {
|
||||
}
|
||||
|
||||
void truncate(BlockSeqno seqno, ConstBlockHandle handle, td::Promise<td::Unit> promise) override {
|
||||
UNREACHABLE();
|
||||
}
|
||||
void wait_shard_client_state(BlockSeqno seqno, td::Timestamp timeout, td::Promise<td::Unit> promise) override {
|
||||
UNREACHABLE();
|
||||
}
|
||||
void log_validator_session_stats(BlockIdExt block_id, validatorsession::ValidatorSessionStats stats) override {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
private:
|
||||
td::Ref<ValidatorManagerOptions> opts_;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "fabric.h"
|
||||
#include "manager.h"
|
||||
#include "validate-broadcast.hpp"
|
||||
#include "ton/ton-tl.hpp"
|
||||
#include "ton/ton-io.hpp"
|
||||
#include "state-serializer.hpp"
|
||||
#include "get-next-key-blocks.h"
|
||||
|
@ -33,14 +34,19 @@
|
|||
|
||||
#include "auto/tl/lite_api.h"
|
||||
#include "tl-utils/lite-utils.hpp"
|
||||
#include "auto/tl/ton_api_json.h"
|
||||
#include "tl/tl_json.h"
|
||||
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "td/utils/JsonBuilder.h"
|
||||
|
||||
#include "common/delay.h"
|
||||
|
||||
#include "validator/stats-merger.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace validator {
|
||||
|
@ -2489,6 +2495,24 @@ void ValidatorManagerImpl::prepare_stats(td::Promise<std::vector<std::pair<std::
|
|||
td::actor::send_closure(db_, &Db::prepare_stats, merger.make_promise("db."));
|
||||
}
|
||||
|
||||
void ValidatorManagerImpl::prepare_perf_timer_stats(td::Promise<std::vector<PerfTimerStats>> promise) {
|
||||
promise.set_value(std::vector<PerfTimerStats>(perf_timer_stats));
|
||||
}
|
||||
|
||||
void ValidatorManagerImpl::add_perf_timer_stat(std::string name, double duration) {
|
||||
for (auto &s : perf_timer_stats) {
|
||||
if (s.name == name) {
|
||||
double now = td::Time::now();
|
||||
while (!s.stats.empty() && s.stats.front().first < now - 3600.0) {
|
||||
s.stats.pop_front();
|
||||
}
|
||||
s.stats.push_back({td::Time::now(), duration});
|
||||
return;
|
||||
}
|
||||
}
|
||||
perf_timer_stats.push_back({name, {{td::Time::now(), duration}}});
|
||||
}
|
||||
|
||||
void ValidatorManagerImpl::truncate(BlockSeqno seqno, ConstBlockHandle handle, td::Promise<td::Unit> promise) {
|
||||
td::actor::send_closure(db_, &Db::truncate, seqno, std::move(handle), std::move(promise));
|
||||
}
|
||||
|
@ -2511,6 +2535,39 @@ void ValidatorManagerImpl::wait_shard_client_state(BlockSeqno seqno, td::Timesta
|
|||
shard_client_waiters_[seqno].waiting_.emplace_back(timeout, 0, std::move(promise));
|
||||
}
|
||||
|
||||
void ValidatorManagerImpl::log_validator_session_stats(BlockIdExt block_id,
|
||||
validatorsession::ValidatorSessionStats stats) {
|
||||
std::string fname = opts_->get_session_logs_file();
|
||||
if (fname.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<tl_object_ptr<ton_api::validatorSession_statsRound>> rounds;
|
||||
for (const auto& round : stats.rounds) {
|
||||
std::vector<tl_object_ptr<ton_api::validatorSession_statsProducer>> producers;
|
||||
for (const auto& producer : round.producers) {
|
||||
producers.push_back(create_tl_object<ton_api::validatorSession_statsProducer>(
|
||||
producer.id.bits256_value(), producer.block_status, producer.block_timestamp));
|
||||
}
|
||||
rounds.push_back(create_tl_object<ton_api::validatorSession_statsRound>(round.timestamp, std::move(producers)));
|
||||
}
|
||||
|
||||
auto obj = create_tl_object<ton_api::validatorSession_stats>(
|
||||
create_tl_block_id_simple(block_id.id), stats.timestamp, stats.self.bits256_value(),
|
||||
stats.creator.bits256_value(), stats.total_validators, stats.total_weight, stats.signatures,
|
||||
stats.signatures_weight, stats.approve_signatures, stats.approve_signatures_weight, stats.first_round,
|
||||
std::move(rounds));
|
||||
std::string s = td::json_encode<std::string>(td::ToJson(*obj.get()), false);
|
||||
s.erase(std::remove_if(s.begin(), s.end(), [](char c) { return c == '\n' || c == '\r'; }), s.end());
|
||||
|
||||
std::ofstream file;
|
||||
file.open(fname, std::ios_base::app);
|
||||
file << s << "\n";
|
||||
file.close();
|
||||
|
||||
LOG(INFO) << "Writing validator session stats for " << block_id.id;
|
||||
}
|
||||
|
||||
td::actor::ActorOwn<ValidatorManagerInterface> ValidatorManagerFactory::create(
|
||||
td::Ref<ValidatorManagerOptions> opts, std::string db_root, td::actor::ActorId<keyring::Keyring> keyring,
|
||||
td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<rldp::Rldp> rldp,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue