1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-12 19:22:37 +00:00

Merge pull request #480 from ton-blockchain/testnet

Merge dev branch
This commit is contained in:
EmelyanenkoK 2022-10-03 09:25:27 +03:00 committed by GitHub
commit 4b940f8bad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
106 changed files with 3403 additions and 478 deletions

79
.github/script/fift-func-wasm-build-ubuntu.sh vendored Executable file
View 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

View file

@ -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)

View file

@ -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));

View file

@ -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)

View file

@ -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) {

View file

@ -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 ]

View file

@ -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;
}

View file

@ -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));

View file

@ -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) {

View file

@ -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

View 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)

View 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)

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View 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
-}

View file

@ -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
View 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);
}

View file

@ -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

View file

@ -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

View file

@ -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
View 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
View 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
View 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"));
}

View 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());
}
}

View file

@ -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 == '`') {

View file

@ -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

View file

@ -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;

View file

@ -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()));

View file

@ -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) {

View file

@ -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) {

View file

@ -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));

View file

@ -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);
};

View file

@ -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:

View file

@ -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};
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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();

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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));
}

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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}

View file

@ -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");

View file

@ -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);

View file

@ -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));

View file

@ -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();
}

View file

@ -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_;
};

View file

@ -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]);

View file

@ -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);

View file

@ -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));

View file

@ -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) {

View file

@ -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_]);
}
}

View file

@ -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

View file

@ -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(

View file

@ -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,

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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();

View file

@ -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);

View file

@ -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>

View file

@ -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,

View file

@ -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() {

View file

@ -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

View file

@ -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);

View file

@ -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");

View file

@ -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;

View file

@ -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);
}

View file

@ -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_;

View file

@ -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_;

View file

@ -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