1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

initial commit

This commit is contained in:
initial commit 2019-09-07 14:03:22 +04:00 committed by vvaltman
commit c2da007f40
1610 changed files with 398047 additions and 0 deletions

333
crypto/CMakeLists.txt Normal file
View file

@ -0,0 +1,333 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
set(TON_CRYPTO_SOURCE
Ed25519.cpp
common/bigint.cpp
common/refcnt.cpp
common/refint.cpp
common/bitstring.cpp
common/util.cpp
ellcurve/Ed25519.cpp
ellcurve/Fp25519.cpp
ellcurve/Montgomery.cpp
ellcurve/TwEdwards.cpp
openssl/bignum.cpp
openssl/residue.cpp
openssl/rand.cpp
vm/stack.cpp
vm/atom.cpp
vm/continuation.cpp
vm/dict.cpp
vm/dispatch.cpp
vm/opctable.cpp
vm/cp0.cpp
vm/stackops.cpp
vm/tupleops.cpp
vm/arithops.cpp
vm/cellops.cpp
vm/contops.cpp
vm/dictops.cpp
vm/debugops.cpp
vm/tonops.cpp
vm/boc.cpp
tl/tlblib.cpp
Ed25519.h
common/AtomicRef.h
common/bigint.hpp
common/bitstring.h
common/refcnt.hpp
common/refint.h
common/util.h
ellcurve/Ed25519.h
ellcurve/Fp25519.h
ellcurve/Montgomery.h
ellcurve/TwEdwards.h
openssl/bignum.h
openssl/digest.h
openssl/rand.hpp
openssl/residue.h
tl/tlbc-aux.h
tl/tlbc-data.h
tl/tlblib.hpp
vm/arithops.h
vm/atom.h
vm/boc.h
vm/box.hpp
vm/cellops.h
vm/continuation.h
vm/contops.h
vm/cp0.h
vm/debugops.h
vm/dict.h
vm/dictops.h
vm/excno.hpp
vm/fmt.hpp
vm/log.h
vm/opctable.h
vm/stack.hpp
vm/stackops.h
vm/tupleops.h
vm/tonops.h
vm/vmstate.h
vm/cells.h
vm/cellslice.h
vm/cells/Cell.cpp
vm/cells/CellBuilder.cpp
vm/cells/CellHash.cpp
vm/cells/CellSlice.cpp
vm/cells/CellTraits.cpp
vm/cells/CellUsageTree.cpp
vm/cells/DataCell.cpp
vm/cells/LevelMask.cpp
vm/cells/MerkleProof.cpp
vm/cells/MerkleUpdate.cpp
vm/cells/Cell.h
vm/cells/CellBuilder.h
vm/cells/CellHash.h
vm/cells/CellSlice.h
vm/cells/CellTraits.h
vm/cells/CellUsageTree.h
vm/cells/CellWithStorage.h
vm/cells/DataCell.h
vm/cells/ExtCell.h
vm/cells/LevelMask.h
vm/cells/MerkleProof.h
vm/cells/MerkleUpdate.h
vm/cells/PrunnedCell.h
vm/cells/UsageCell.h
vm/cells/VirtualCell.h
vm/cells/VirtualizationParameters.h
vm/db/StaticBagOfCellsDb.h
vm/db/StaticBagOfCellsDb.cpp
vm/db/BlobView.h
vm/db/BlobView.cpp
)
set(TON_DB_SOURCE
vm/db/DynamicBagOfCellsDb.cpp
vm/db/CellStorage.cpp
vm/db/TonDb.cpp
vm/db/DynamicBagOfCellsDb.h
vm/db/CellHashTable.h
vm/db/CellStorage.h
vm/db/TonDb.h
)
set(FIFT_SOURCE
fift/Dictionary.cpp
fift/Fift.cpp
fift/IntCtx.cpp
fift/SourceLookup.cpp
fift/utils.cpp
fift/words.cpp
fift/Dictionary.h
fift/Fift.h
fift/IntCtx.h
fift/SourceLookup.h
fift/utils.h
fift/words.h
)
set(PARSER_SOURCE
parser/srcread.cpp
parser/lexer.cpp
parser/symtable.cpp
parser/srcread.h
parser/lexer.h
parser/symtable.h
)
set(FUNC_LIB_SOURCE
func/keywords.cpp
func/unify-types.cpp
func/parse-func.cpp
func/abscode.cpp
func/gen-abscode.cpp
func/analyzer.cpp
func/asmops.cpp
func/builtins.cpp
func/stack-transform.cpp
func/optimize.cpp
func/codegen.cpp
)
set(TLB_BLOCK_AUTO
${CMAKE_CURRENT_SOURCE_DIR}/block/block-auto.cpp
${CMAKE_CURRENT_SOURCE_DIR}/block/block-auto.h
)
set(BLOCK_SOURCE
block/Binlog.h
block/Binlog.cpp
block/block.cpp
block/block-db.cpp
block/block-parse.cpp
block/check-proof.cpp
block/mc-config.cpp
block/output-queue-merger.cpp
block/transaction.cpp
${TLB_BLOCK_AUTO}
block/block-binlog.h
block/block-db-impl.h
block/block-db.h
block/block.h
block/block-parse.h
block/check-proof.h
block/output-queue-merger.h
block/transaction.h
)
set(ED25519_TEST_SOURCE
${CMAKE_CURRENT_SOURCE_DIR}/test/Ed25519.cpp
PARENT_SCOPE
)
set(TONDB_TEST_SOURCE
${CMAKE_CURRENT_SOURCE_DIR}/test/test-db.cpp
PARENT_SCOPE
)
set(CELLS_TEST_SOURCE
${CMAKE_CURRENT_SOURCE_DIR}/test/test-cells.cpp
PARENT_SCOPE
)
set(TONVM_TEST_SOURCE
${CMAKE_CURRENT_SOURCE_DIR}/test/vm.cpp
PARENT_SCOPE
)
set(FIFT_TEST_SOURCE
${CMAKE_CURRENT_SOURCE_DIR}/test/fift.cpp
PARENT_SCOPE
)
add_library(ton_crypto STATIC ${TON_CRYPTO_SOURCE})
target_include_directories(ton_crypto PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(ton_crypto PUBLIC ${OPENSSL_CRYPTO_LIBRARY} tdutils)
if (NOT WIN32)
target_link_libraries(ton_crypto PUBLIC dl z)
endif()
target_include_directories(ton_crypto SYSTEM PUBLIC ${OPENSSL_INCLUDE_DIR})
add_library(ton_db STATIC ${TON_DB_SOURCE})
target_include_directories(ton_db PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(ton_db PUBLIC tdutils tddb ton_crypto)
add_executable(test-ed25519-crypto test/test-ed25519-crypto.cpp)
target_include_directories(test-ed25519-crypto PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(test-ed25519-crypto PUBLIC ton_crypto)
add_library(fift-lib ${FIFT_SOURCE})
target_include_directories(fift-lib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(fift-lib PUBLIC ton_crypto ton_db tdutils ton_block)
set_target_properties(fift-lib PROPERTIES OUTPUT_NAME fift)
add_executable(fift fift/fift-main.cpp)
target_link_libraries(fift PUBLIC fift-lib)
if (WINGETOPT_FOUND)
target_link_libraries_system(fift wingetopt)
endif()
add_library(src_parser ${PARSER_SOURCE})
target_include_directories(src_parser PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(src_parser PUBLIC ton_crypto)
add_executable(func func/func.cpp ${FUNC_LIB_SOURCE})
target_include_directories(func PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(func PUBLIC ton_crypto src_parser)
if (WINGETOPT_FOUND)
target_link_libraries_system(func wingetopt)
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)
if (WINGETOPT_FOUND)
target_link_libraries_system(tlbc wingetopt)
endif()
add_library(ton_block ${BLOCK_SOURCE})
target_include_directories(ton_block PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/block> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(ton_block PUBLIC ton_crypto tdutils tdactor tl_api)
set(TURN_OFF_LSAN cd .)
if (TON_USE_ASAN AND NOT WIN32)
set(TURN_OFF_LSAN export LSAN_OPTIONS=detect_leaks=0)
endif()
if (NOT CMAKE_CROSSCOMPILING)
set(GENERATE_TLB_CMD tlbc)
add_custom_command(
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/block
COMMAND ${TURN_OFF_LSAN}
COMMAND ${GENERATE_TLB_CMD} -o block-auto -n block::gen -z block.tlb
COMMENT "Generate block tlb source files"
OUTPUT ${TLB_BLOCK_AUTO}
DEPENDS tlbc block/block.tlb
)
add_custom_target(tlb_generate_block DEPENDS ${TLB_BLOCK_AUTO})
add_dependencies(ton_block tlb_generate_block)
add_custom_target(gen_fif ALL)
function(GenFif)
set(options )
set(oneValueArgs DEST)
set(multiValueArgs SOURCE)
set(FUNC_LIB_SOURCE smartcont/stdlib.fc)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
string(REGEX REPLACE "[^a-zA-Z_]" "_" ID ${ARG_DEST})
add_custom_command(
COMMENT "Generate ${ARG_DEST}"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND func -PS -o ${ARG_DEST} ${FUNC_LIB_SOURCE} ${ARG_SOURCE}
MAIN_DEPENDENCY ${ARG_SOURCE}
DEPENDS func ${FUNC_LIB_SOURCE}
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${ARG_DEST}
)
add_custom_target(gen_fif_${ID} DEPENDS ${ARG_DEST})
add_dependencies(gen_fif gen_fif_${ID})
endfunction()
GenFif(DEST smartcont/config-code.fif SOURCE smartcont/config-code.fc)
GenFif(DEST smartcont/wallet-code.fif SOURCE smartcont/wallet-code.fc)
GenFif(DEST smartcont/simple-wallet-code.fif SOURCE smartcont/simple-wallet-code.fc)
GenFif(DEST smartcont/elector-code.fif SOURCE smartcont/elector-code.fc)
endif()
add_executable(create-state block/create-state.cpp)
target_include_directories(create-state PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(create-state PUBLIC ton_crypto fift-lib ton_block)
if (WINGETOPT_FOUND)
target_link_libraries_system(create-state wingetopt)
endif()
add_executable(test-block block/test-block.cpp)
target_include_directories(test-block PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(test-block PUBLIC ton_crypto fift-lib ton_block)
if (WINGETOPT_FOUND)
target_link_libraries_system(test-block wingetopt)
endif()

394
crypto/Ed25519.cpp Normal file
View file

@ -0,0 +1,394 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "crypto/Ed25519.h"
#if TD_HAVE_OPENSSL
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER >= 0x10101000L && OPENSSL_VERSION_NUMBER != 0x20000000L
#include "td/utils/base64.h"
#include "td/utils/BigNum.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/ScopeGuard.h"
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#else
#include "crypto/ellcurve/Ed25519.h"
#endif
namespace td {
Ed25519::PublicKey::PublicKey(SecureString octet_string) : octet_string_(std::move(octet_string)) {
}
SecureString Ed25519::PublicKey::as_octet_string() const {
return octet_string_.copy();
}
Ed25519::PrivateKey::PrivateKey(SecureString octet_string) : octet_string_(std::move(octet_string)) {
}
SecureString Ed25519::PrivateKey::as_octet_string() const {
return octet_string_.copy();
}
#if OPENSSL_VERSION_NUMBER >= 0x10101000L && OPENSSL_VERSION_NUMBER != 0x20000000L
namespace detail {
static Result<SecureString> X25519_key_from_PKEY(EVP_PKEY *pkey, bool is_private) {
auto func = is_private ? &EVP_PKEY_get_raw_private_key : &EVP_PKEY_get_raw_public_key;
size_t len = 0;
if (func(pkey, nullptr, &len) == 0) {
return Status::Error("Failed to get raw key length");
}
CHECK(len == 32);
SecureString result(len);
if (func(pkey, result.as_mutable_slice().ubegin(), &len) == 0) {
return Status::Error("Failed to get raw key");
}
return std::move(result);
}
static EVP_PKEY *X25519_key_to_PKEY(Slice key, bool is_private) {
auto func = is_private ? &EVP_PKEY_new_raw_private_key : &EVP_PKEY_new_raw_public_key;
return func(EVP_PKEY_ED25519, nullptr, key.ubegin(), key.size());
}
static Result<SecureString> X25519_pem_from_PKEY(EVP_PKEY *pkey, bool is_private, Slice password) {
BIO *mem_bio = BIO_new(BIO_s_mem());
SCOPE_EXIT {
BIO_vfree(mem_bio);
};
if (is_private) {
PEM_write_bio_PrivateKey(mem_bio, pkey, EVP_aes_256_cbc(), const_cast<unsigned char *>(password.ubegin()),
narrow_cast<int>(password.size()), nullptr, nullptr);
} else {
PEM_write_bio_PUBKEY(mem_bio, pkey);
}
char *data_ptr = nullptr;
auto data_size = BIO_get_mem_data(mem_bio, &data_ptr);
return std::string(data_ptr, data_size);
}
static int password_cb(char *buf, int size, int rwflag, void *u) {
auto &password = *reinterpret_cast<Slice *>(u);
auto password_size = narrow_cast<int>(password.size());
if (size < password_size) {
return -1;
}
if (rwflag == 0) {
MutableSlice(buf, size).copy_from(password);
}
return password_size;
}
static EVP_PKEY *X25519_pem_to_PKEY(Slice pem, Slice password) {
BIO *mem_bio = BIO_new_mem_buf(pem.ubegin(), narrow_cast<int>(pem.size()));
SCOPE_EXIT {
BIO_vfree(mem_bio);
};
return PEM_read_bio_PrivateKey(mem_bio, nullptr, password_cb, &password);
}
} // namespace detail
Result<Ed25519::PrivateKey> Ed25519::generate_private_key() {
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(NID_ED25519, nullptr);
if (pctx == nullptr) {
return Status::Error("Can't create EVP_PKEY_CTX");
}
SCOPE_EXIT {
EVP_PKEY_CTX_free(pctx);
};
if (EVP_PKEY_keygen_init(pctx) <= 0) {
return Status::Error("Can't init keygen");
}
EVP_PKEY *pkey = nullptr;
if (EVP_PKEY_keygen(pctx, &pkey) <= 0) {
return Status::Error("Can't generate random private key");
}
SCOPE_EXIT {
EVP_PKEY_free(pkey);
};
TRY_RESULT(private_key, detail::X25519_key_from_PKEY(pkey, true));
return std::move(private_key);
}
Result<Ed25519::PublicKey> Ed25519::PrivateKey::get_public_key() const {
auto pkey = detail::X25519_key_to_PKEY(octet_string_, true);
if (pkey == nullptr) {
return Status::Error("Can't import private key");
}
SCOPE_EXIT {
EVP_PKEY_free(pkey);
};
TRY_RESULT(key, detail::X25519_key_from_PKEY(pkey, false));
return Ed25519::PublicKey(std::move(key));
}
Result<SecureString> Ed25519::PrivateKey::as_pem(Slice password) const {
auto pkey = detail::X25519_key_to_PKEY(octet_string_, true);
if (pkey == nullptr) {
return Status::Error("Can't import private key");
}
SCOPE_EXIT {
EVP_PKEY_free(pkey);
};
return detail::X25519_pem_from_PKEY(pkey, true, password);
}
Result<Ed25519::PrivateKey> Ed25519::PrivateKey::from_pem(Slice pem, Slice password) {
auto pkey = detail::X25519_pem_to_PKEY(pem, password);
if (pkey == nullptr) {
return Status::Error("Can't import private key from pem");
}
TRY_RESULT(key, detail::X25519_key_from_PKEY(pkey, true));
return Ed25519::PrivateKey(std::move(key));
}
Result<SecureString> Ed25519::PrivateKey::sign(Slice data) const {
auto pkey = detail::X25519_key_to_PKEY(octet_string_, true);
if (pkey == nullptr) {
return Status::Error("Can't import private key");
}
SCOPE_EXIT {
EVP_PKEY_free(pkey);
};
EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
if (md_ctx == nullptr) {
return Status::Error("Can't create EVP_MD_CTX");
}
SCOPE_EXIT {
EVP_MD_CTX_free(md_ctx);
};
if (EVP_DigestSignInit(md_ctx, nullptr, nullptr, nullptr, pkey) <= 0) {
return Status::Error("Can't init DigestSign");
}
SecureString res(64, '\0');
size_t len = 64;
if (EVP_DigestSign(md_ctx, res.as_mutable_slice().ubegin(), &len, data.ubegin(), data.size()) <= 0) {
return Status::Error("Can't sign data");
}
return std::move(res);
}
Status Ed25519::PublicKey::verify_signature(Slice data, Slice signature) const {
auto pkey = detail::X25519_key_to_PKEY(octet_string_, false);
if (pkey == nullptr) {
return Status::Error("Can't import public key");
}
SCOPE_EXIT {
EVP_PKEY_free(pkey);
};
EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
if (md_ctx == nullptr) {
return Status::Error("Can't create EVP_MD_CTX");
}
SCOPE_EXIT {
EVP_MD_CTX_free(md_ctx);
};
if (EVP_DigestVerifyInit(md_ctx, nullptr, nullptr, nullptr, pkey) <= 0) {
return Status::Error("Can't init DigestVerify");
}
if (EVP_DigestVerify(md_ctx, signature.ubegin(), signature.size(), data.ubegin(), data.size())) {
return Status::OK();
}
return Status::Error("Wrong signature");
}
Result<SecureString> Ed25519::compute_shared_secret(const PublicKey &public_key, const PrivateKey &private_key) {
BigNum p = BigNum::from_hex("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed").move_as_ok();
auto public_y = public_key.as_octet_string();
public_y.as_mutable_slice()[31] = static_cast<char>(public_y[31] & 127);
BigNum y = BigNum::from_le_binary(public_y);
BigNum y2 = y.clone();
y += 1;
y2 -= 1;
BigNumContext context;
BigNum::mod_sub(y2, p, y2, p, context);
BigNum inverse_y_plus_1;
BigNum::mod_inverse(inverse_y_plus_1, y2, p, context);
BigNum u;
BigNum::mod_mul(u, y, inverse_y_plus_1, p, context);
auto pr_key = private_key.as_octet_string();
unsigned char buf[64];
SHA512(Slice(pr_key).ubegin(), 32, buf);
buf[0] &= 248;
buf[31] &= 127;
buf[31] |= 64;
auto pkey_private = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, nullptr, buf, 32);
if (pkey_private == nullptr) {
return Status::Error("Can't import private key");
}
SCOPE_EXIT {
EVP_PKEY_free(pkey_private);
};
// LOG(ERROR) << buffer_to_hex(Slice(buf, 32));
auto pub_key = u.to_le_binary(32);
auto pkey_public = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, nullptr, Slice(pub_key).ubegin(), pub_key.size());
if (pkey_public == nullptr) {
return Status::Error("Can't import public key");
}
SCOPE_EXIT {
EVP_PKEY_free(pkey_public);
};
// LOG(ERROR) << buffer_to_hex(pub_key);
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey_private, nullptr);
if (ctx == nullptr) {
return Status::Error("Can't create EVP_PKEY_CTX");
}
SCOPE_EXIT {
EVP_PKEY_CTX_free(ctx);
};
if (EVP_PKEY_derive_init(ctx) <= 0) {
return Status::Error("Can't init derive");
}
if (EVP_PKEY_derive_set_peer(ctx, pkey_public) <= 0) {
return Status::Error("Can't init derive");
}
size_t result_len = 0;
if (EVP_PKEY_derive(ctx, nullptr, &result_len) <= 0) {
return Status::Error("Can't get result length");
}
if (result_len != 32) {
return Status::Error("Unexpected result length");
}
SecureString result(result_len, '\0');
if (EVP_PKEY_derive(ctx, result.as_mutable_slice().ubegin(), &result_len) <= 0) {
return Status::Error("Failed to compute shared secret");
}
return std::move(result);
}
#else
Result<Ed25519::PrivateKey> Ed25519::generate_private_key() {
crypto::Ed25519::PrivateKey private_key;
if (!private_key.random_private_key(true)) {
return Status::Error("Can't generate random private key");
}
SecureString private_key_buf(32);
if (!private_key.export_private_key(private_key_buf.as_mutable_slice())) {
return Status::Error("Failed to export private key");
}
return PrivateKey(std::move(private_key_buf));
}
Result<Ed25519::PublicKey> Ed25519::PrivateKey::get_public_key() const {
crypto::Ed25519::PrivateKey private_key;
if (!private_key.import_private_key(Slice(octet_string_).ubegin())) {
return Status::Error("Bad private key");
}
SecureString public_key(32);
if (!private_key.get_public_key().export_public_key(public_key.as_mutable_slice())) {
return Status::Error("Failed to export public key");
}
return PublicKey(std::move(public_key));
}
Result<SecureString> Ed25519::PrivateKey::as_pem(Slice password) const {
return Status::Error("Not supported");
}
Result<Ed25519::PrivateKey> Ed25519::PrivateKey::from_pem(Slice pem, Slice password) {
return Status::Error("Not supported");
}
Result<SecureString> Ed25519::PrivateKey::sign(Slice data) const {
crypto::Ed25519::PrivateKey private_key;
if (!private_key.import_private_key(Slice(octet_string_).ubegin())) {
return Status::Error("Bad private key");
}
SecureString signature(crypto::Ed25519::sign_bytes, '\0');
if (!private_key.sign_message(signature.as_mutable_slice(), data)) {
return Status::Error("Failed to sign message");
}
return std::move(signature);
}
Status Ed25519::PublicKey::verify_signature(Slice data, Slice signature) const {
if (signature.size() != crypto::Ed25519::sign_bytes) {
return Status::Error("Signature has invalid length");
}
crypto::Ed25519::PublicKey public_key;
if (!public_key.import_public_key(Slice(octet_string_).ubegin())) {
return Status::Error("Bad public key");
}
if (public_key.check_message_signature(signature, data)) {
return Status::OK();
}
return Status::Error("Wrong signature");
}
Result<SecureString> Ed25519::compute_shared_secret(const PublicKey &public_key, const PrivateKey &private_key) {
crypto::Ed25519::PrivateKey tmp_private_key;
if (!tmp_private_key.import_private_key(Slice(private_key.as_octet_string()).ubegin())) {
return Status::Error("Bad private key");
}
crypto::Ed25519::PublicKey tmp_public_key;
if (!tmp_public_key.import_public_key(Slice(public_key.as_octet_string()).ubegin())) {
return Status::Error("Bad public key");
}
SecureString shared_secret(32, '\0');
if (!tmp_private_key.compute_shared_secret(shared_secret.as_mutable_slice(), tmp_public_key)) {
return Status::Error("Failed to compute shared secret");
}
return std::move(shared_secret);
}
#endif
} // namespace td
#endif

72
crypto/Ed25519.h Normal file
View file

@ -0,0 +1,72 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/SharedSlice.h"
#include "td/utils/Status.h"
#if TD_HAVE_OPENSSL
namespace td {
class Ed25519 {
public:
class PublicKey {
public:
static constexpr size_t LENGTH = 32;
explicit PublicKey(SecureString octet_string);
SecureString as_octet_string() const;
Status verify_signature(Slice data, Slice signature) const;
private:
SecureString octet_string_;
};
class PrivateKey {
public:
static constexpr size_t LENGTH = 32;
explicit PrivateKey(SecureString octet_string);
SecureString as_octet_string() const;
Result<PublicKey> get_public_key() const;
Result<SecureString> sign(Slice data) const;
Result<SecureString> as_pem(Slice password) const;
static Result<PrivateKey> from_pem(Slice pem, Slice password);
private:
SecureString octet_string_;
};
static Result<PrivateKey> generate_private_key();
static Result<SecureString> compute_shared_secret(const PublicKey &public_key, const PrivateKey &private_key);
};
} // namespace td
#endif

493
crypto/block/Binlog.cpp Normal file
View file

@ -0,0 +1,493 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "crypto/block/Binlog.h"
#include "td/utils/as.h"
#include "td/utils/misc.h"
#include "td/utils/port/path.h"
#include <sstream>
namespace block {
/*
*
* GENERIC BINLOG (move to separate file)
*
*/
BinlogBuffer::BinlogBuffer(std::unique_ptr<BinlogCallback> cb, std::size_t _max_size, td::FileFd fd)
: cb(std::move(cb))
, need_more_bytes(0)
, eptr(nullptr)
, log_rpos(0)
, log_cpos(0)
, log_wpos(0)
, fd(std::move(fd))
, replica(false)
, writing(false)
, dirty(false)
, created(false)
, ok(false) {
max_size = _max_size;
start = static_cast<unsigned char*>(std::malloc(max_size));
DCHECK(start);
rptr = wptr = cptr = start;
end = start + max_size;
}
unsigned char* BinlogBuffer::alloc_log_event_force(std::size_t size) {
unsigned char* res = alloc_log_event(size);
if (!res) {
throw LevAllocError{size};
}
return res;
}
unsigned char* BinlogBuffer::try_alloc_log_event(std::size_t size) {
if (!eptr) {
if (end - wptr >= (long)size) {
unsigned char* res = wptr;
wptr += size;
log_wpos += size;
return res;
}
eptr = wptr;
wptr = start;
if (rptr == eptr) {
rptr = start;
}
if (cptr == eptr) {
cptr = start;
}
}
if (rptr - wptr > (long)size) {
unsigned char* res = wptr;
wptr += size;
log_wpos += size;
return res;
}
return nullptr;
}
bool BinlogBuffer::flush(int mode) {
auto r_res = try_flush(mode);
if (r_res.is_ok()) {
return r_res.ok();
}
std::string msg = PSTRING() << "cannot flush binlog file " << binlog_name << " at position " << log_rpos << " "
<< r_res.error();
LOG(ERROR) << msg;
throw BinlogError{msg};
}
td::Result<bool> BinlogBuffer::try_flush(int mode) {
LOG(DEBUG) << "in flush: writing=" << writing << " r=" << rptr - start << " c=" << cptr - start
<< " w=" << wptr - start << "; rp=" << log_rpos << " cp=" << log_cpos << " wp=" << log_wpos;
if (!writing || rptr == cptr) {
return false; // nothing to flush
}
DCHECK(!fd.empty()); // must have an open binlog file
while (rptr != cptr) {
unsigned char* tptr = (cptr >= rptr ? cptr : eptr);
DCHECK(rptr <= tptr);
auto sz = tptr - rptr;
if (sz) {
LOG(INFO) << "writing " << sz << " bytes to binlog " << binlog_name << " at position " << log_rpos;
TRY_RESULT(res, fd.pwrite(td::Slice(rptr, sz), log_rpos));
if (static_cast<td::int64>(res) != sz) {
return td::Status::Error(PSLICE() << "written " << res << " bytes instead of " << sz);
}
log_rpos += sz;
rptr += sz;
}
if (rptr == eptr) {
rptr = start;
eptr = nullptr;
}
}
if (mode >= 3) {
LOG(INFO) << "syncing binlog " << binlog_name << " (position " << log_rpos << ")";
TRY_STATUS(fd.sync());
}
return true;
}
unsigned char* BinlogBuffer::alloc_log_event(std::size_t size) {
if (!writing) {
throw BinlogError{"cannot create new binlog event: binlog not open for writing"};
}
if (size >= max_size || size > max_event_size) {
return nullptr;
}
size = (size + 3) & -4;
unsigned char* res = try_alloc_log_event(size);
if (!res) {
flush();
return try_alloc_log_event(size);
} else {
return res;
}
}
bool BinlogBuffer::commit_range(unsigned long long pos_start, unsigned long long pos_end) {
// TODO: make something more clever, with partially committed/uncommitted segments in [cpos..wpos] range
if (pos_start != log_cpos || pos_end < pos_start || pos_end > log_wpos) {
return false;
}
if (!pos_start && pos_end >= pos_start + 4 && td::as<unsigned>(cptr) != 0x0442446b) {
throw BinlogError{"incorrect magic"};
}
long long size = pos_end - pos_start;
replay_range(cptr, pos_start, pos_end);
log_cpos = pos_end;
cptr += size;
if (eptr && cptr >= eptr) {
cptr -= eptr - start;
}
return true;
}
bool BinlogBuffer::rollback_range(unsigned long long pos_start, unsigned long long pos_end) {
if (pos_start < log_cpos || pos_end < pos_start || pos_end != log_wpos) {
return false;
}
long long size = pos_end - pos_start;
log_wpos = pos_end;
if (size >= wptr - start) {
wptr -= size;
} else {
DCHECK(eptr);
wptr += eptr - start - size;
}
return true;
}
void BinlogBuffer::NewBinlogEvent::commit() {
//LOG(DEBUG) << "in NewBinlogEvent::commit (status = " << status << ")";
if (!(status & 4)) {
throw BinlogError{"cannot commit new binlog event: already committed or rolled back"};
}
if (!bb.commit_range(pos, pos + size)) {
throw BinlogError{"cannot commit new binlog event: possibly some earlier log events are not committed yet"};
}
status = 1;
//LOG(DEBUG) << "after NewBinlogEvent::commit (status = " << status << ")";
}
void BinlogBuffer::NewBinlogEvent::rollback() {
if (!(status & 4)) {
throw BinlogError{"cannot roll back new binlog event: already committed or rolled back"};
}
if (!bb.rollback_range(pos, pos + size)) {
throw BinlogError{"cannot roll back new binlog event: possibly some later log event are already committed"};
}
status = 2;
}
BinlogBuffer::NewBinlogEvent::~NewBinlogEvent() {
if (status & 4) {
if (status == 5) {
status = 4;
commit();
} else if (status == 6) {
status = 4;
rollback();
} else {
LOG(ERROR) << "newly-allocated binlog event is neither committed nor rolled back (automatically rolling back)";
rollback();
}
}
}
void BinlogBuffer::replay_range(unsigned char* ptr, unsigned long long pos_start, unsigned long long pos_end) {
unsigned char* tptr = (ptr <= wptr ? wptr : eptr);
long long avail = tptr - ptr;
while (pos_start < pos_end) {
if (ptr == eptr) {
ptr = start;
tptr = wptr;
avail = tptr - ptr;
if (avail > (long long)(pos_end - pos_start)) {
avail = pos_end - pos_start;
}
}
int res = (avail >= 4 ? cb->replay_log_event(*this, reinterpret_cast<const unsigned*>(ptr),
td::narrow_cast<size_t>(avail), pos_start)
: -0x7ffffffc);
if (res <= 0 || res > avail) {
std::ostringstream ss;
ss << "cannot interpret newly-committed binlog event 0x" << std::hex
<< (avail >= 4 ? (unsigned)td::as<unsigned>(ptr) : 0u) << std::dec << ": error " << res;
throw BinlogError{ss.str()};
}
ptr += res;
pos_start += res;
avail -= res;
}
}
int BinlogBuffer::replay_pending(bool allow_partial) {
if (rptr == cptr) {
return 0;
}
unsigned char* tptr = (rptr <= cptr ? cptr : eptr);
long long avail = tptr - rptr;
DCHECK(tptr && avail >= 0);
while (rptr != cptr) {
int res = (avail >= 4 ? cb->replay_log_event(*this, reinterpret_cast<const unsigned*>(rptr),
td::narrow_cast<size_t>(avail), log_rpos)
: -0x7ffffffc);
if (res > 0) {
if (res > avail) {
throw BinlogError{"binlog event used more bytes than available"};
}
avail -= res;
log_rpos += res;
rptr += res;
if (rptr != eptr) {
continue;
}
rptr = start;
tptr = cptr;
avail = tptr - rptr;
continue;
}
long long prev_need = 0;
while (res < -0x40000000) {
long long need = res - 0x80000000;
need = (need + 3) & -4;
if (need > (long long)max_event_size) {
throw BinlogError{"binlog event requires too many bytes"};
}
if (need <= avail) {
throw BinlogError{"binlog event requires more bytes, but we already had them"};
}
if (need <= prev_need) {
throw BinlogError{"binlog event requires more bytes, but we already had them"};
}
prev_need = need;
long long total_avail = avail + (rptr > cptr ? cptr - start : 0);
if (need > total_avail) {
if (allow_partial) {
need_more_bytes = td::narrow_cast<size_t>(need - total_avail);
return 2;
} else {
throw BinlogError{"binlog event extends past end of buffer"};
}
}
if (need <= 1024) {
unsigned char tmp[1024];
std::memcpy(tmp, rptr, td::narrow_cast<size_t>(avail));
std::memcpy(tmp + avail, start, td::narrow_cast<size_t>(need - avail));
res = cb->replay_log_event(*this, reinterpret_cast<const unsigned*>(tmp), td::narrow_cast<size_t>(need),
log_rpos);
} else {
unsigned char* tmp = static_cast<unsigned char*>(std::malloc(td::narrow_cast<size_t>(need)));
std::memcpy(tmp, rptr, td::narrow_cast<size_t>(avail));
std::memcpy(tmp + avail, start, td::narrow_cast<size_t>(need - avail));
res = cb->replay_log_event(*this, reinterpret_cast<const unsigned*>(tmp), td::narrow_cast<size_t>(need),
log_rpos);
std::free(tmp);
}
if (res > need) {
throw BinlogError{"binlog event used more bytes than available"};
}
}
if (res < 0) {
return res;
}
if (!res) {
throw BinlogError{"unknown error while interpreting binlog event"};
}
if (res < avail) {
avail -= res;
log_rpos += res;
rptr += res;
continue;
}
DCHECK(eptr);
log_rpos += res;
rptr += res;
rptr = start + (rptr - eptr);
eptr = nullptr;
DCHECK(start <= rptr && rptr <= cptr && cptr <= wptr && wptr <= end);
}
return 1;
}
BinlogBuffer::~BinlogBuffer() {
if (start) {
if (writing) {
flush(2);
}
std::free(start);
}
}
td::Status BinlogBuffer::set_binlog(std::string new_binlog_name, int mode) {
if (!binlog_name.empty() || !fd.empty()) {
return td::Status::Error("binlog buffer already attached to a file");
}
td::int32 flags = td::FileFd::Read;
if ((mode & 1) != 0) {
flags |= td::FileFd::Write;
}
auto r_fd = td::FileFd::open(new_binlog_name, flags, 0640);
if (r_fd.is_error()) {
if (!(~mode & 3)) {
TRY_RESULT(new_fd, td::FileFd::open(new_binlog_name, flags | td::FileFd::CreateNew, 0640));
fd = std::move(new_fd);
created = true;
} else {
return r_fd.move_as_error();
}
} else {
fd = r_fd.move_as_ok();
}
replica = !(mode & 1);
if (!replica) {
TRY_STATUS(fd.lock(td::FileFd::LockFlags::Write, new_binlog_name, 100));
}
if (created) {
writing = true;
td::Status res;
try {
res = cb->init_new_binlog(*this);
} catch (BinlogBuffer::BinlogError& err) {
res = td::Status::Error(err.msg);
}
if (res.is_error()) {
fd.close();
td::unlink(new_binlog_name).ignore();
writing = false;
return res;
}
binlog_name = new_binlog_name;
ok = true;
return td::Status::OK();
}
binlog_name = new_binlog_name;
auto res = replay_binlog(replica);
if (res.is_error()) {
return res.move_as_error();
}
if (!replica) {
if (log_rpos != log_wpos || log_rpos != log_cpos || rptr != wptr || rptr != cptr) {
std::string msg = (PSLICE() << "error while interpreting binlog `" << binlog_name << "`: " << log_wpos - log_rpos
<< " bytes left uninterpreted at position " << log_rpos << ", truncated binlog?")
.c_str();
LOG(ERROR) << msg;
return td::Status::Error(msg);
}
//rptr = wptr = cptr = start;
//eptr = nullptr;
LOG(INFO) << "read and interpreted " << res.move_as_ok() << " bytes from binlog `" << binlog_name
<< "`, final position " << log_rpos << ", reopening in write mode";
writing = true;
if (!log_rpos) {
td::Status status;
try {
status = cb->init_new_binlog(*this);
} catch (BinlogBuffer::BinlogError& err) {
status = td::Status::Error(err.msg);
}
if (status.is_error()) {
fd.close();
td::unlink(new_binlog_name).ignore();
writing = false;
return status;
}
}
}
ok = true;
return td::Status::OK();
}
td::Result<long long> BinlogBuffer::replay_binlog(bool allow_partial) {
if (writing) {
return 0;
}
long long total = 0;
while (true) {
auto res = read_file();
if (res.is_error()) {
return res.move_as_error();
}
long long sz = res.move_as_ok();
total += sz;
try {
cptr = wptr;
log_cpos = log_wpos;
if (!log_rpos && rptr == start && wptr >= rptr + 4 && td::as<unsigned>(rptr) != 0x0442446b) {
throw BinlogError{"incorrect magic"};
}
int r = replay_pending(allow_partial || sz != 0);
if (r < 0 && r >= -0x40000000) {
throw InterpretError{(PSLICE() << "binlog error " << r).c_str()};
}
} catch (BinlogError err) {
LOG(ERROR) << "error reading binlog " << binlog_name << ": " << err.msg << " at position " << log_rpos;
return td::Status::Error(PSLICE() << "error reading binlog " << binlog_name << ": " << err.msg << " at position "
<< log_rpos);
} catch (InterpretError err) {
LOG(ERROR) << "error interpreting binlog " << binlog_name << ": " << err.msg << " at position " << log_rpos;
return td::Status::Error(PSLICE() << "error interpreting binlog " << binlog_name << ": " << err.msg
<< " at position " << log_rpos);
}
if (!sz) {
break;
}
};
return total;
}
td::Result<int> BinlogBuffer::read_file() {
unsigned char* ptr = wptr;
std::size_t sz = end - wptr;
if (rptr > wptr) {
DCHECK(eptr);
sz = rptr - wptr;
if (sz <= 4) {
return 0; // buffer full
}
sz -= 4;
} else if (!sz) {
DCHECK(!eptr);
if (rptr <= start + 4) {
return 0; // buffer full
}
eptr = end;
ptr = wptr = start;
sz = rptr - start - 4;
}
auto r_res = fd.pread(td::MutableSlice(ptr, sz), log_wpos);
if (r_res.is_error()) {
std::string msg = PSTRING() << "error reading binlog file `" << binlog_name << "` at position " << log_wpos << " : "
<< r_res.error();
LOG(ERROR) << msg;
return td::Status::Error(msg);
}
auto res = r_res.move_as_ok();
DCHECK(std::size_t(res) <= sz);
LOG(INFO) << "read " << res << " bytes from binlog `" << binlog_name << "` at position " << log_wpos;
log_wpos += res;
wptr += res;
return (int)res;
}
} // namespace block

157
crypto/block/Binlog.h Normal file
View file

@ -0,0 +1,157 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/Status.h"
#include "td/utils/port/FileFd.h"
namespace block {
/*
*
* BINLOG (BUFFERS) -- move to separate file
*
*/
class BinlogBuffer;
class BinlogCallback {
public:
virtual ~BinlogCallback() = default;
virtual td::Status init_new_binlog(BinlogBuffer& bb) = 0;
virtual int replay_log_event(BinlogBuffer& bb, const unsigned* ptr, std::size_t len, unsigned long long pos) = 0;
};
class BinlogBuffer {
static constexpr std::size_t max_event_size = 0xfffc;
std::unique_ptr<BinlogCallback> cb;
std::size_t max_size;
std::size_t need_more_bytes;
unsigned char *start, *rptr, *cptr, *wptr, *eptr, *end;
unsigned long long log_rpos, log_cpos, log_wpos;
std::string binlog_name;
td::FileFd fd;
bool replica;
bool writing;
bool dirty;
bool created;
bool ok;
td::Result<int> read_file();
td::Result<long long> replay_binlog(bool allow_partial);
unsigned char* try_alloc_log_event(std::size_t size);
int replay_pending(bool allow_partial = false);
void replay_range(unsigned char* ptr, unsigned long long pos_start, unsigned long long pos_end);
td::Status set_fd(td::FileFd fd);
public:
struct LevAllocError {
std::size_t size;
explicit LevAllocError(std::size_t _size) : size(_size) {
}
};
struct InterpretError {
std::string msg;
explicit InterpretError(std::string _msg) : msg(_msg) {
}
};
struct BinlogError {
std::string msg;
explicit BinlogError(std::string _msg) : msg(_msg) {
}
};
BinlogBuffer(std::unique_ptr<BinlogCallback> _cb, std::size_t _max_size = (1 << 24), td::FileFd fd = {});
BinlogBuffer(const BinlogBuffer&) = delete;
BinlogBuffer& operator=(const BinlogBuffer&) = delete;
BinlogBuffer(BinlogBuffer&&) = delete;
BinlogBuffer& operator=(BinlogBuffer&&) = delete;
~BinlogBuffer();
td::Status set_binlog(std::string _binlog_name, int mode = 0);
unsigned char* alloc_log_event(std::size_t size);
unsigned char* alloc_log_event_force(std::size_t size);
bool flush(int mode = 0);
td::Result<bool> try_flush(int mode);
unsigned long long get_rpos() const {
return log_rpos;
}
//
class NewBinlogEvent {
protected:
BinlogBuffer& bb;
unsigned long long pos;
unsigned size;
int status;
public:
NewBinlogEvent(BinlogBuffer& _bb, unsigned long long _pos, unsigned _size)
: bb(_bb), pos(_pos), size(_size), status(4) {
}
~NewBinlogEvent();
unsigned long long get_log_pos() const {
return pos;
}
void commit();
void commit_later() {
if (status & 4) {
status = 5;
}
}
void rollback();
void rollback_later() {
if (status & 4) {
status = 6;
}
};
};
template <typename T>
class NewEvent : public NewBinlogEvent {
T* ptr;
protected:
friend class BinlogBuffer;
NewEvent(BinlogBuffer& _bb, unsigned long long _pos, unsigned _size, T* _ptr)
: NewBinlogEvent(_bb, _pos, _size), ptr(_ptr) {
}
public:
T* operator->() const {
return ptr;
}
T& operator*() const {
return *ptr;
}
void commit() {
NewBinlogEvent::commit();
ptr = nullptr;
}
void rollback() {
NewBinlogEvent::rollback();
ptr = nullptr;
}
};
template <typename T, typename... Args>
NewEvent<T> alloc(Args... args) {
unsigned long long pos = log_wpos;
return NewEvent<T>(*this, pos, sizeof(T), new (alloc_log_event_force(sizeof(T))) T(args...));
}
protected:
friend class NewBinlogEvent;
bool commit_range(unsigned long long pos_start, unsigned long long pos_end);
bool rollback_range(unsigned long long pos_start, unsigned long long pos_end);
};
} // namespace block

111
crypto/block/block-binlog.h Normal file
View file

@ -0,0 +1,111 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/int_types.h"
#include "ton/ton-types.h"
#include <ctime>
namespace block {
namespace log {
#pragma pack(push, 4)
struct Start {
static constexpr unsigned tag = 0x0442446b;
static constexpr unsigned log_type = 0x290100;
unsigned tag_field;
unsigned type_field;
unsigned created_at;
unsigned char zerostate_root_hash[32];
Start(const RootHash& hash, unsigned _now = 0)
: tag_field(tag), type_field(log_type), created_at(_now ? _now : (unsigned)std::time(nullptr)) {
td::as<RootHash>(zerostate_root_hash) = hash;
}
};
struct SetZeroState {
static constexpr unsigned tag = 0x63ab3cd9;
unsigned tag_field;
unsigned flags;
long long file_size;
unsigned char file_hash[32];
unsigned char root_hash[32];
SetZeroState(const RootHash& rhash, const FileHash& fhash, unsigned long long _fsize, unsigned _flags = 0)
: tag_field(tag), flags(_flags), file_size(_fsize) {
td::as<FileHash>(file_hash) = fhash;
td::as<RootHash>(root_hash) = rhash;
}
};
struct NewBlock {
static constexpr unsigned tag = 0x19f4bc63;
unsigned tag_field;
unsigned flags; // lower 8 bits = authority
int workchain;
unsigned seqno;
unsigned long long shard;
long long file_size;
unsigned char file_hash[32];
unsigned char root_hash[32];
unsigned char last_bytes[8];
NewBlock(const ton::BlockId& block, const RootHash& rhash, const FileHash& fhash, unsigned long long _fsize,
unsigned _flags)
: tag_field(tag)
, flags(_flags)
, workchain(block.workchain)
, seqno(block.seqno)
, shard(block.shard)
, file_size(_fsize) {
td::as<FileHash>(file_hash) = fhash;
td::as<RootHash>(root_hash) = rhash;
td::as<unsigned long long>(last_bytes) = 0;
}
};
struct NewState {
static constexpr unsigned tag = 0x4190a21f;
unsigned tag_field;
unsigned flags; // lower 8 bits = authority
int workchain;
unsigned seqno;
unsigned long long shard;
long long file_size;
unsigned char file_hash[32];
unsigned char root_hash[32];
unsigned char last_bytes[8];
NewState(const ton::BlockId& state, const RootHash& rhash, const FileHash& fhash, unsigned long long _fsize,
unsigned _flags)
: tag_field(tag)
, flags(_flags)
, workchain(state.workchain)
, seqno(state.seqno)
, shard(state.shard)
, file_size(_fsize) {
td::as<FileHash>(file_hash) = fhash;
td::as<RootHash>(root_hash) = rhash;
td::as<unsigned long long>(last_bytes) = 0;
}
};
#pragma pack(pop)
} // namespace log
} // namespace block

View file

@ -0,0 +1,135 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <string>
#include <map>
#include "vm/cells.h"
#include "block/Binlog.h"
#include "block/block-db.h"
#include "block/block-binlog.h"
namespace block {
using td::Ref;
/*
*
* BLOCK DATABASE
*
*/
class BlockDbImpl;
class BlockBinlogCallback : public BinlogCallback {
BlockDbImpl& db;
td::Status init_new_binlog(BinlogBuffer& bb) override;
int replay_log_event(BinlogBuffer& bb, const unsigned* ptr, std::size_t len, unsigned long long log_pos) override;
int replay(const block::log::Start& lev, unsigned long long log_pos) const;
int replay(const block::log::SetZeroState& lev, unsigned long long log_pos) const;
int replay(const block::log::NewBlock& lev, unsigned long long log_pos) const;
int replay(const block::log::NewState& lev, unsigned long long log_pos) const;
template <typename T>
inline int try_interpret(const unsigned* ptr, std::size_t len, unsigned long long log_pos);
public:
BlockBinlogCallback(BlockDbImpl& _db) : db(_db) {
}
};
template <typename T>
inline int BlockBinlogCallback::try_interpret(const unsigned* ptr, std::size_t len, unsigned long long log_pos) {
if (len < sizeof(T)) {
return 0x80000000 + sizeof(T);
} else {
int res = replay(*reinterpret_cast<const T*>(ptr), log_pos);
return res >= 0 ? sizeof(T) : res;
}
}
class BlockDbImpl final : public BlockDb {
int status;
bool allow_uninit;
bool created;
int depth;
std::unique_ptr<ZerostateInfo> zstate;
std::string base_dir;
std::string binlog_name;
BinlogBuffer bb;
ton::Bits256 zstate_rhash, zstate_fhash;
unsigned created_at;
std::map<ton::FileHash, td::BufferSlice> file_cache;
std::map<ton::BlockId, Ref<FileInfo>> block_info;
std::map<ton::BlockId, Ref<FileInfo>> state_info;
//
td::Result<int> do_init();
public:
enum FMode {
chk_none = 0,
chk_if_exists = 1,
fail_if_exists = 2,
overwrite = 4,
chk_size_only = 16,
chk_file_hash = 32
};
static constexpr const char* default_binlog_name = "blockdb";
static constexpr const char* default_binlog_suffix = ".bin";
static constexpr int default_depth = 4;
BlockDbImpl(td::Result<int>& _res, std::string _base_dir, std::unique_ptr<ZerostateInfo> _zstate = nullptr,
bool _allow_uninit = false, int _depth = 4, std::string _binlog_name = "");
~BlockDbImpl();
bool ok() const {
return status >= 0;
}
bool initialized() const {
return status != 0;
}
bool init_ok() const {
return status > 0;
}
protected:
friend class BlockBinlogCallback;
td::Ref<FileInfo> zerostate;
td::Status init_from_zstate();
td::Status update_block_info(Ref<FileInfo> blk_info);
td::Status update_state_info(Ref<FileInfo> state);
private:
std::string compute_db_filename(const FileHash& file_hash) const;
std::string compute_db_tmp_filename(const FileHash& file_hash, int i, bool makedirs) const;
td::Status save_db_file(const FileHash& file_hash, const td::BufferSlice& data, int fmode = 0);
td::Status load_data(FileInfo& file_info, bool force = false);
// actor BlockDb implementation
void get_top_block_id(ton::ShardIdFull shard, int authority, td::Promise<ton::BlockIdExt> promise) override;
void get_top_block_state_id(ton::ShardIdFull shard, int authority, td::Promise<ton::BlockIdExt> promise) override;
void get_block_by_id(ton::BlockId blk_id, bool need_data, td::Promise<td::Ref<FileInfo>> promise) override;
void get_state_by_id(ton::BlockId blk_id, bool need_data, td::Promise<td::Ref<FileInfo>> promise) override;
void get_out_queue_info_by_id(ton::BlockId blk_id, td::Promise<td::Ref<OutputQueueInfoDescr>> promise) override;
void get_object_by_file_hash(FileHash file_hash, bool need_data, bool force_file_load,
td::Promise<td::Ref<FileInfo>> promise) override;
void get_object_by_root_hash(RootHash root_hash, bool need_data, bool force_file_load,
td::Promise<td::Ref<FileInfo>> promise) override;
void save_new_block(ton::BlockIdExt blk_id, td::BufferSlice data, int authority,
td::Promise<td::Unit> promise) override;
void save_new_state(ton::BlockIdExt state_id, td::BufferSlice data, int authority,
td::Promise<td::Unit> promise) override;
bool file_cache_insert(const FileHash& file_hash, const td::BufferSlice& data, int mode = 0);
};
} // namespace block

835
crypto/block/block-db.cpp Normal file
View file

@ -0,0 +1,835 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "block-db.h"
#include "block-db-impl.h"
#include "block-binlog.h"
#include "td/utils/common.h"
#include "td/utils/crypto.h"
#include "td/utils/format.h"
#include "td/utils/misc.h"
#include "td/utils/port/FileFd.h"
#include "td/utils/port/path.h"
#include "td/utils/filesystem.h"
#include "vm/cellslice.h"
#include "vm/boc.h"
#include "vm/db/StaticBagOfCellsDb.h"
#include <limits>
namespace block {
//static constexpr std::string default_binlog_name = "blockdb";
//static constexpr std::string default_binlog_suffix = ".bin";
bool parse_hash_string(std::string arg, RootHash& res) {
if (arg.size() != 64) {
res.set_zero();
return false;
}
int f = 1;
unsigned char* ptr = res.data();
for (char c : arg) {
f <<= 4;
if (c >= '0' && c <= '9') {
f += c - '0';
} else {
c |= 0x20;
if (c >= 'a' && c <= 'f') {
f += c - ('a' - 10);
} else {
res.set_zero();
return false;
}
}
if (f >= 0x100) {
*ptr++ = (unsigned char)f;
f = 1;
}
}
return true;
}
td::Result<td::BufferSlice> load_binary_file(std::string filename, td::int64 max_size) {
//TODO: use td::read_file
auto res = [&]() -> td::Result<td::BufferSlice> {
TRY_RESULT(fd, td::FileFd::open(filename, td::FileFd::Read));
TRY_RESULT(stat, fd.stat());
if (!stat.is_reg_) {
return td::Status::Error("file is not regular");
}
td::int64 size = stat.size_;
if (!size) {
return td::Status::Error("file is empty");
}
if ((max_size && size > max_size) || static_cast<td::uint64>(size) > std::numeric_limits<std::size_t>::max()) {
return td::Status::Error("file is too long");
}
td::BufferSlice res(td::narrow_cast<std::size_t>(size));
TRY_RESULT(r, fd.read(res.as_slice()));
if (r != static_cast<td::uint64>(size)) {
return td::Status::Error(PSLICE() << "read " << r << " bytes out of " << size);
}
return std::move(res);
}();
LOG_IF(ERROR, res.is_error()) << "error reading file `" << filename << "` : " << res.error();
return res;
}
td::Status save_binary_file(std::string filename, const td::BufferSlice& data, unsigned long long max_size) {
//TODO: use td::write_file
auto status = [&]() {
if (max_size && data.size() > max_size) {
return td::Status::Error("contents too long");
}
auto size = data.size();
TRY_RESULT(to_file, td::FileFd::open(filename, td::FileFd::CreateNew | td::FileFd::Write));
TRY_RESULT(written, to_file.write(data));
if (written != static_cast<size_t>(size)) {
return td::Status::Error(PSLICE() << "written " << written << " bytes instead of " << size);
}
to_file.close();
return td::Status::OK();
}();
LOG_IF(ERROR, status.is_error()) << "error writing new file `" << filename << "` : " << status;
return status;
}
FileHash compute_file_hash(const td::BufferSlice& data) {
ton::Bits256 data_hash;
td::sha256(data, td::MutableSlice{data_hash.data(), 32});
return data_hash;
}
FileHash compute_file_hash(td::Slice data) {
ton::Bits256 data_hash;
td::sha256(data, td::MutableSlice{data_hash.data(), 32});
return data_hash;
}
/*
*
* ZEROSTATE CONFIGURATION
*
*/
td::Status ZerostateInfo::base_check() {
if (!has_data()) {
return td::Status::OK();
}
auto data_hash = compute_file_hash(data);
if (!has_file_hash()) {
file_hash = data_hash;
} else if (file_hash != data_hash) {
return td::Status::Error("zerostate file hash mismatch");
}
vm::BagOfCells boc;
auto res = boc.deserialize(data);
if (!res.is_ok() || boc.get_root_count() != 1) {
return td::Status::Error("zerostate is not a valid bag of cells"); // not a valid bag-of-Cells
}
data_hash = boc.get_root_cell()->get_hash().bits();
if (!has_root_hash()) {
root_hash = data_hash;
} else if (root_hash != data_hash) {
return td::Status::Error("zerostate root hash mismatch");
}
return td::Status::OK();
}
/*
*
* BLOCK DATABASE
*
*/
std::string compute_db_filename(std::string base_dir, const FileHash& file_hash, int depth) {
static const char hex_digits[] = "0123456789ABCDEF";
assert(depth >= 0 && depth <= 8);
std::string res = std::move(base_dir);
res.reserve(res.size() + 32 + depth * 3 + 4);
for (int i = 0; i < depth; i++) {
unsigned u = file_hash.data()[i];
res.push_back(hex_digits[u >> 4]);
res.push_back(hex_digits[u & 15]);
res.push_back('/');
}
for (int i = 0; i < 32; i++) {
unsigned u = file_hash.data()[i];
res.push_back(hex_digits[u >> 4]);
res.push_back(hex_digits[u & 15]);
}
res += ".boc";
return res;
}
std::string BlockDbImpl::compute_db_filename(const FileHash& file_hash) const {
return block::compute_db_filename(base_dir, file_hash, depth);
}
std::string compute_db_tmp_filename(std::string base_dir, const FileHash& file_hash, int i, bool makedirs, int depth) {
static const char hex_digits[] = "0123456789ABCDEF";
assert(depth >= 0 && depth <= 8);
std::string res = std::move(base_dir);
res.reserve(res.size() + 32 + depth * 3 + 4);
for (int j = 0; j < depth; j++) {
unsigned u = file_hash.data()[j];
res.push_back(hex_digits[u >> 4]);
res.push_back(hex_digits[u & 15]);
res.push_back('/');
if (makedirs) {
td::mkdir(res, 0755).ignore();
}
}
for (int j = 0; j < 32; j++) {
unsigned u = file_hash.data()[j];
res.push_back(hex_digits[u >> 4]);
res.push_back(hex_digits[u & 15]);
}
res += ".tmp";
if (i > 0) {
if (i < 10) {
res.push_back((char)('0' + i));
} else {
res.push_back((char)('0' + i / 10));
res.push_back((char)('0' + i % 10));
}
}
return res;
}
std::string BlockDbImpl::compute_db_tmp_filename(const FileHash& file_hash, int i, bool makedirs) const {
return block::compute_db_tmp_filename(base_dir, file_hash, i, makedirs, depth);
}
bool BlockDbImpl::file_cache_insert(const FileHash& file_hash, const td::BufferSlice& data, int mode) {
auto it = file_cache.find(file_hash);
if (it != file_cache.end()) {
// found
return true;
}
auto res = file_cache.emplace(file_hash, data.clone());
return res.second;
}
td::Status BlockDbImpl::save_db_file(const FileHash& file_hash, const td::BufferSlice& data, int fmode) {
if (fmode & FMode::chk_file_hash && file_hash != compute_file_hash(data)) {
return td::Status::Error("file hash passed for creation of a new file does not match contents");
}
std::string filename = compute_db_filename(file_hash);
bool overwrite = false;
auto r_stat = td::stat(filename);
if (r_stat.is_ok()) {
auto stat = r_stat.move_as_ok();
// file exists
if (fmode & FMode::fail_if_exists) {
return td::Status::Error(PSLICE() << "file " << filename << " cannot be created, it already exists");
}
if (!(fmode & (FMode::chk_if_exists | FMode::overwrite))) {
file_cache_insert(file_hash, data);
return td::Status::OK();
}
if (fmode & FMode::chk_if_exists) {
if (stat.size_ != (long long)data.size()) {
LOG(ERROR) << "file " << filename << " already exists with wrong content";
if (!(fmode & FMode::overwrite)) {
return td::Status::Error(PSLICE() << "file " << filename << " already exists with wrong content");
}
} else if (fmode & FMode::chk_size_only) {
file_cache_insert(file_hash, data);
return td::Status::OK();
} else {
auto res = load_binary_file(filename);
if (res.is_error()) {
return res.move_as_error();
}
auto old_contents = res.move_as_ok();
if (old_contents.size() != data.size() || old_contents.as_slice() != data.as_slice()) {
LOG(ERROR) << "file " << filename << " already exists with wrong content";
if (!(fmode & FMode::overwrite)) {
return td::Status::Error(PSLICE() << "file " << filename << " already exists with wrong content");
}
} else {
file_cache_insert(file_hash, data);
return td::Status::OK();
}
}
}
overwrite = true;
}
std::string tmp_filename;
for (int i = 0; i < 10; i++) {
tmp_filename = compute_db_tmp_filename(file_hash, i, true);
auto res = save_binary_file(tmp_filename, data);
if (res.is_ok()) {
break;
}
if (i == 9) {
return res;
}
}
auto rename_status = td::rename(tmp_filename, filename);
if (rename_status.is_error()) {
td::unlink(tmp_filename).ignore();
LOG(ERROR) << rename_status;
return rename_status;
}
if (overwrite) {
LOG(DEBUG) << "database file `" << filename << "` overwritten, " << data.size() << " bytes";
} else {
LOG(DEBUG) << "new database file `" << filename << "` created, " << data.size() << " bytes";
}
file_cache_insert(file_hash, data);
return td::Status::OK();
}
td::Result<td::actor::ActorOwn<BlockDb>> BlockDb::create_block_db(std::string base_dir,
std::unique_ptr<ZerostateInfo> zstate,
bool allow_uninit, int depth,
std::string binlog_name) {
using td::actor::ActorId;
using td::actor::ActorOwn;
td::Result<int> res;
ActorOwn<BlockDbImpl> actor =
td::actor::create_actor<BlockDbImpl>(td::actor::ActorOptions().with_name("BlockDB"), res, base_dir,
std::move(zstate), allow_uninit, depth, binlog_name);
if (res.is_error()) {
return std::move(res).move_as_error();
} else {
return std::move(actor);
}
}
BlockDbImpl::BlockDbImpl(td::Result<int>& _res, std::string _base_dir, std::unique_ptr<ZerostateInfo> _zstate,
bool _allow_uninit, int _depth, std::string _binlog_name)
: status(0)
, allow_uninit(_allow_uninit)
, created(false)
, depth(_depth)
, zstate(std::move(_zstate))
, base_dir(_base_dir)
, binlog_name(_binlog_name)
, bb(std::unique_ptr<BinlogCallback>(new BlockBinlogCallback(*this)))
, created_at(0) {
auto res = do_init();
status = (res.is_ok() && res.ok() > 0 ? res.ok() : -1);
if (res.is_error()) {
_res = std::move(res);
} else {
_res = res.move_as_ok();
}
}
td::Result<int> BlockDbImpl::do_init() {
if (base_dir.empty()) {
return td::Status::Error("block database cannot have empty base directory");
}
if (depth < 0 || depth >= 8) {
return td::Status::Error("block database directory tree depth must be in range 0..8");
}
if (base_dir.back() != '/') {
base_dir.push_back('/');
}
if (binlog_name.empty()) {
binlog_name = default_binlog_name;
}
bool f = true;
for (char c : binlog_name) {
if (c == '.') {
f = false;
} else if (c == '/') {
f = true;
}
}
if (f) {
binlog_name += default_binlog_suffix;
}
if (binlog_name.at(0) != '/') {
binlog_name = base_dir + binlog_name;
}
if (zstate) {
if (!zstate->has_data() && zstate->has_filename()) {
auto data = load_binary_file(zstate->filename, 1 << 20);
if (data.is_error()) {
return data.move_as_error();
}
zstate->data = data.move_as_ok();
}
auto res = zstate->base_check();
if (res.is_error()) {
return res;
}
}
try {
auto res = bb.set_binlog(binlog_name, allow_uninit ? 3 : 1);
if (res.is_error()) {
return res;
}
} catch (BinlogBuffer::BinlogError& err) {
return td::Status::Error(-2, std::string{"error while initializing block database binlog: "} + err.msg);
} catch (BinlogBuffer::InterpretError& err) {
return td::Status::Error(-3, std::string{"error while interpreting block database binlog: "} + err.msg);
}
return created;
}
BlockDbImpl::~BlockDbImpl() {
}
td::Status BlockDbImpl::init_from_zstate() {
if (!zstate) {
return td::Status::Error("no zero state provided, cannot initialize from scratch");
}
if (!zstate->has_data()) {
if (zstate->has_filename() || zstate->has_file_hash()) {
if (!zstate->has_filename()) {
zstate->filename = compute_db_filename(zstate->file_hash);
}
auto res = load_binary_file(zstate->filename, 1 << 20);
if (res.is_error()) {
return res.move_as_error();
}
zstate->data = res.move_as_ok();
} else {
return td::Status::Error("cannot load zero state for block DB creation");
}
}
auto res = zstate->base_check();
if (res.is_error()) {
return res;
}
assert(zstate->has_file_hash() && zstate->has_root_hash());
res = save_db_file(zstate->file_hash, zstate->data, FMode::chk_if_exists | FMode::chk_file_hash);
if (res.is_error()) {
return res;
}
return res;
}
td::Status BlockBinlogCallback::init_new_binlog(BinlogBuffer& bb) {
auto res = db.init_from_zstate();
if (res.is_error()) {
return res;
}
auto lev = bb.alloc<log::Start>(db.zstate->root_hash);
assert(!lev.get_log_pos());
auto lev2 = bb.alloc<log::SetZeroState>(db.zstate->root_hash, db.zstate->file_hash, db.zstate->data.size());
lev.commit();
lev2.commit(); // TODO: introduce multi-commit bb.commit(lev, lev2)
bb.flush(3);
db.created = true;
return td::Status::OK();
}
#define REPLAY_CASE(__T) \
case __T::tag: \
return try_interpret<__T>(ptr, len, log_pos);
int BlockBinlogCallback::replay_log_event(BinlogBuffer& bb, const unsigned* ptr, std::size_t len,
unsigned long long log_pos) {
assert(len >= 4);
LOG(DEBUG) << "replay_log_event(" << len << ", " << log_pos << ", " << *ptr << ")";
switch (*ptr) {
REPLAY_CASE(log::Start);
REPLAY_CASE(log::SetZeroState);
REPLAY_CASE(log::NewBlock);
REPLAY_CASE(log::NewState);
}
std::ostringstream ss;
ss << "unknown binlog event 0x" << std::hex << *ptr << std::dec;
LOG(ERROR) << ss.str() << " at position " << log_pos;
throw BinlogBuffer::InterpretError{ss.str()};
}
#undef REPLAY_CASE
int BlockBinlogCallback::replay(const log::Start& lev, unsigned long long log_pos) const {
LOG(DEBUG) << "in replay(Start{" << lev.tag_field << ", " << lev.type_field << ", " << lev.created_at << "})";
if (lev.type_field != lev.log_type) {
throw BinlogBuffer::InterpretError{(PSLICE() << "unsupported binlog type " << lev.type_field).str()};
}
if (log_pos) {
throw BinlogBuffer::InterpretError{"LEV_START can only be the very first record in a binlog"};
}
db.zstate_rhash = lev.zerostate_root_hash;
db.created_at = lev.created_at;
if (db.zstate) {
if (!db.zstate->has_root_hash()) {
db.zstate->root_hash = db.zstate_rhash;
} else if (db.zstate->root_hash != db.zstate_rhash) {
throw BinlogBuffer::InterpretError{PSTRING() << "zerostate hash mismatch: in binlog " << db.zstate_rhash.to_hex()
<< ", required " << db.zstate->root_hash.to_hex()};
}
}
return 0; // ok
}
int BlockBinlogCallback::replay(const log::SetZeroState& lev, unsigned long long log_pos) const {
LOG(DEBUG) << "in replay(SetZeroState)";
// LOG(DEBUG) << "db.zstate_rhash = " << db.zstate_rhash.to_hex();
if (db.zstate_rhash != td::ConstBitPtr{lev.root_hash}) {
throw BinlogBuffer::InterpretError{std::string{"SetZeroState: zerostate root hash mismatch: in binlog "} +
ton::Bits256{lev.root_hash}.to_hex() + ", required " + db.zstate_rhash.to_hex()};
}
db.zerostate = td::Ref<FileInfo>{true,
FileType::state,
ton::BlockId{ton::masterchainId, 1ULL << 63, 0},
0,
td::as<FileHash>(lev.file_hash),
td::as<RootHash>(lev.root_hash),
lev.file_size};
return 0; // ok
}
int BlockBinlogCallback::replay(const log::NewBlock& lev, unsigned long long log_pos) const {
LOG(DEBUG) << "in replay(NewBlock)";
if (!lev.seqno || lev.workchain == ton::workchainInvalid) {
return -1;
}
ton::BlockId blkid{lev.workchain, lev.shard, lev.seqno};
auto blk_info = td::Ref<FileInfo>{true,
FileType::block,
blkid,
lev.flags & 0xff,
td::as<FileHash>(lev.file_hash),
td::as<RootHash>(lev.root_hash),
lev.file_size};
auto res = db.update_block_info(blk_info);
if (res.is_error()) {
LOG(ERROR) << "cannot update block information in the local DB: " << res.to_string();
return -1;
} else {
return 0; // ok
}
}
int BlockBinlogCallback::replay(const log::NewState& lev, unsigned long long log_pos) const {
LOG(DEBUG) << "in replay(NewState)";
if (!lev.seqno || lev.workchain == ton::workchainInvalid) {
return -1;
}
ton::BlockId id{lev.workchain, lev.shard, lev.seqno};
auto state_info = td::Ref<FileInfo>{true,
FileType::state,
id,
lev.flags & 0xff,
td::as<FileHash>(lev.file_hash),
td::as<RootHash>(lev.root_hash),
lev.file_size};
auto res = db.update_state_info(state_info);
if (res.is_error()) {
LOG(ERROR) << "cannot update shardchain state information in the local DB: " << res.to_string();
return -1;
} else {
return 0; // ok
}
}
td::Status BlockDbImpl::update_block_info(Ref<FileInfo> blk_info) {
auto it = block_info.find(blk_info->blk.id);
if (it != block_info.end()) {
// already exists
if (it->second->blk.file_hash != blk_info->blk.file_hash || it->second->blk.root_hash != blk_info->blk.root_hash) {
return td::Status::Error(-666, std::string{"fatal error in block DB: block "} + blk_info->blk.id.to_str() +
" has two records with different file or root hashes");
} else {
return td::Status::OK();
}
} else {
auto id = blk_info->blk.id;
auto res = block_info.emplace(std::move(id), std::move(blk_info));
if (res.second) {
return td::Status::OK();
} else {
return td::Status::Error(-666, "cannot insert block information into DB");
}
}
}
td::Status BlockDbImpl::update_state_info(Ref<FileInfo> state) {
auto it = state_info.find(state->blk.id);
if (it != state_info.end()) {
// already exists
if (it->second->blk.root_hash != state->blk.root_hash) {
return td::Status::Error(-666, std::string{"fatal error in block DB: state for block "} + state->blk.id.to_str() +
" has two records with different root hashes");
} else {
return td::Status::OK();
}
} else {
auto id = state->blk.id;
auto res = state_info.emplace(std::move(id), std::move(state));
if (res.second) {
return td::Status::OK();
} else {
return td::Status::Error(-666, "cannot insert state information into DB");
}
}
}
void BlockDbImpl::get_top_block_id(ton::ShardIdFull shard, int authority, td::Promise<ton::BlockIdExt> promise) {
LOG(DEBUG) << "in BlockDb::get_top_block_id()";
auto it = block_info.upper_bound(ton::BlockId{shard, std::numeric_limits<td::uint32>::max()});
if (it != block_info.begin() && ton::ShardIdFull{(--it)->first} == shard) {
promise(it->second->blk);
return;
}
if (shard.is_masterchain()) {
promise(ton::BlockIdExt{ton::BlockId{ton::masterchainId, 1ULL << 63, 0}});
return;
}
promise(td::Status::Error(-666));
}
void BlockDbImpl::get_top_block_state_id(ton::ShardIdFull shard, int authority, td::Promise<ton::BlockIdExt> promise) {
LOG(DEBUG) << "in BlockDb::get_top_block_state_id()";
auto it = state_info.upper_bound(ton::BlockId{shard, std::numeric_limits<td::uint32>::max()});
if (it != state_info.begin() && ton::ShardIdFull{(--it)->first} == shard) {
promise(it->second->blk);
return;
}
if (shard.is_masterchain() && zerostate.not_null()) {
promise(zerostate->blk);
return;
}
promise(td::Status::Error(-666, "no state for given workchain found in database"));
}
void BlockDbImpl::get_block_by_id(ton::BlockId blk_id, bool need_data, td::Promise<td::Ref<FileInfo>> promise) {
LOG(DEBUG) << "in BlockDb::get_block_by_id({" << blk_id.workchain << ", " << blk_id.shard << ", " << blk_id.seqno
<< "}, " << need_data << ")";
auto it = block_info.find(blk_id);
if (it != block_info.end()) {
if (need_data && it->second->data.is_null()) {
LOG(DEBUG) << "loading data for block " << blk_id.to_str();
auto res = load_data(it->second.write());
if (res.is_error()) {
promise(std::move(res));
return;
}
}
promise(it->second);
}
promise(td::Status::Error(-666, "block not found in database"));
}
void BlockDbImpl::get_state_by_id(ton::BlockId blk_id, bool need_data, td::Promise<td::Ref<FileInfo>> promise) {
LOG(DEBUG) << "in BlockDb::get_state_by_id({" << blk_id.workchain << ", " << blk_id.shard << ", " << blk_id.seqno
<< "}, " << need_data << ")";
auto it = state_info.find(blk_id);
if (it != state_info.end()) {
if (need_data && it->second->data.is_null()) {
LOG(DEBUG) << "loading data for state " << blk_id.to_str();
auto res = load_data(it->second.write());
if (res.is_error()) {
promise(std::move(res));
return;
}
}
promise(it->second);
}
if (zerostate.not_null() && blk_id == zerostate->blk.id) {
LOG(DEBUG) << "get_state_by_id(): zerostate requested";
if (need_data && zerostate->data.is_null()) {
LOG(DEBUG) << "loading data for zerostate";
auto res = load_data(zerostate.write());
if (res.is_error()) {
promise(std::move(res));
return;
}
}
promise(zerostate);
return;
}
promise(td::Status::Error(-666, "requested state not found in database"));
}
void BlockDbImpl::get_out_queue_info_by_id(ton::BlockId blk_id, td::Promise<td::Ref<OutputQueueInfoDescr>> promise) {
LOG(DEBUG) << "in BlockDb::get_out_queue_info_by_id({" << blk_id.workchain << ", " << blk_id.shard << ", "
<< blk_id.seqno << ")";
auto it = state_info.find(blk_id);
if (it == state_info.end()) {
promise(td::Status::Error(
-666, std::string{"cannot obtain output queue info for block "} + blk_id.to_str() + " : cannot load state"));
}
if (it->second->data.is_null()) {
LOG(DEBUG) << "loading data for state " << blk_id.to_str();
auto res = load_data(it->second.write());
if (res.is_error()) {
promise(std::move(res));
return;
}
}
auto it2 = block_info.find(blk_id);
if (it2 == block_info.end()) {
promise(td::Status::Error(-666, std::string{"cannot obtain output queue info for block "} + blk_id.to_str() +
" : cannot load block description"));
}
vm::StaticBagOfCellsDbLazy::Options options;
auto res = vm::StaticBagOfCellsDbLazy::create(it->second->data.clone(), options);
if (res.is_error()) {
td::Status err = res.move_as_error();
LOG(ERROR) << "cannot deserialize state for block " << blk_id.to_str() << " : " << err.to_string();
promise(std::move(err));
return;
}
auto static_boc = res.move_as_ok();
auto rc = static_boc->get_root_count();
if (rc.is_error()) {
promise(rc.move_as_error());
return;
}
if (rc.move_as_ok() != 1) {
promise(td::Status::Error(-668, std::string{"state for block "} + blk_id.to_str() + " is invalid"));
return;
}
auto res3 = static_boc->get_root_cell(0);
if (res3.is_error()) {
promise(res3.move_as_error());
return;
}
auto state_root = res3.move_as_ok();
if (it->second->blk.root_hash != state_root->get_hash().bits()) {
promise(td::Status::Error(
-668, std::string{"state for block "} + blk_id.to_str() + " is invalid : state root hash mismatch"));
}
vm::CellSlice cs = vm::load_cell_slice(state_root);
if (!cs.have(64, 1) || cs.prefetch_ulong(32) != 0x9023afde) {
promise(td::Status::Error(-668, std::string{"state for block "} + blk_id.to_str() + " is invalid"));
}
auto out_queue_info = cs.prefetch_ref();
promise(Ref<OutputQueueInfoDescr>{true, blk_id, it2->second->blk.root_hash.cbits(), state_root->get_hash().bits(),
std::move(out_queue_info)});
}
void BlockDbImpl::get_object_by_file_hash(FileHash file_hash, bool need_data, bool force_file_load,
td::Promise<td::Ref<FileInfo>> promise) {
if (zerostate.not_null() && zerostate->blk.file_hash == file_hash) {
if (need_data && zerostate->data.is_null()) {
auto res = load_data(zerostate.write());
if (res.is_error()) {
promise(std::move(res));
return;
}
}
promise(zerostate);
return;
}
promise(td::Status::Error(-666));
}
void BlockDbImpl::get_object_by_root_hash(RootHash root_hash, bool need_data, bool force_file_load,
td::Promise<td::Ref<FileInfo>> promise) {
if (zerostate.not_null() && zerostate->blk.root_hash == root_hash) {
if (need_data && zerostate->data.is_null()) {
auto res = load_data(zerostate.write());
if (res.is_error()) {
promise(std::move(res));
return;
}
}
promise(zerostate);
return;
}
promise(td::Status::Error(-666));
}
void BlockDbImpl::save_new_block(ton::BlockIdExt id, td::BufferSlice data, int authority,
td::Promise<td::Unit> promise) {
// TODO: add verification that data is a BoC with correct root hash, and that it is a Block corresponding to blk_id
// ...
// TODO: check whether we already have a block with blk_id
// ...
auto save_res = save_db_file(id.file_hash, data, FMode::chk_if_exists | FMode::overwrite | FMode::chk_file_hash);
if (save_res.is_error()) {
promise(std::move(save_res));
}
auto sz = data.size();
auto lev = bb.alloc<log::NewBlock>(id.id, id.root_hash, id.file_hash, data.size(), authority & 0xff);
if (sz <= 8) {
std::memcpy(lev->last_bytes, data.data(), sz);
} else {
std::memcpy(lev->last_bytes, data.data() + sz - 8, 8);
}
lev.commit();
bb.flush();
promise(td::Unit{});
}
void BlockDbImpl::save_new_state(ton::BlockIdExt id, td::BufferSlice data, int authority,
td::Promise<td::Unit> promise) {
// TODO: add verification that data is a BoC with correct root hash, and that it is a Block corresponding to blk_id
// ...
// TODO: check whether we already have a block with blk_id
// ...
auto save_res = save_db_file(id.file_hash, data, FMode::chk_if_exists | FMode::overwrite | FMode::chk_file_hash);
if (save_res.is_error()) {
promise(std::move(save_res));
}
auto sz = data.size();
auto lev = bb.alloc<log::NewState>(id.id, id.root_hash, id.file_hash, data.size(), authority & 0xff);
if (sz <= 8) {
std::memcpy(lev->last_bytes, data.data(), sz);
} else {
std::memcpy(lev->last_bytes, data.data() + sz - 8, 8);
}
lev.commit();
bb.flush();
promise(td::Unit{});
}
td::Status BlockDbImpl::load_data(FileInfo& file_info, bool force) {
if (!file_info.data.is_null() && !force) {
return td::Status::OK();
}
if (file_info.blk.file_hash.is_zero()) {
return td::Status::Error("cannot load a block file without knowing its file hash");
}
auto it = file_cache.find(file_info.blk.file_hash);
if (it != file_cache.end() && !force) {
file_info.data = it->second.clone();
return td::Status::OK();
}
std::string filename = compute_db_filename(file_info.blk.file_hash);
auto res = load_binary_file(filename);
if (res.is_error()) {
return res.move_as_error();
}
file_info.data = res.move_as_ok();
file_cache_insert(file_info.blk.file_hash, file_info.data);
return td::Status::OK();
}
FileInfo FileInfo::clone() const {
return FileInfo{*this};
}
FileInfo::FileInfo(const FileInfo& other)
: td::CntObject()
, blk(other.blk)
, type(other.type)
, status(other.status)
, file_size(other.file_size)
, data(other.data.clone()) {
}
FileInfo* FileInfo::make_copy() const {
return new FileInfo(*this);
}
} // namespace block

164
crypto/block/block-db.h Normal file
View file

@ -0,0 +1,164 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/int_types.h"
#include "td/utils/buffer.h"
#include "td/actor/actor.h"
#include "ton/ton-types.h"
#include "crypto/common/refcnt.hpp"
#include "crypto/vm/cells.h"
namespace block {
using FileHash = ton::Bits256;
using RootHash = ton::Bits256;
using td::Ref;
struct ZerostateInfo {
td::BufferSlice data;
std::string filename;
FileHash file_hash;
RootHash root_hash;
ZerostateInfo() {
file_hash.set_zero();
root_hash.set_zero();
}
ZerostateInfo(RootHash hash, std::string _filename) : filename(_filename), root_hash(std::move(hash)) {
file_hash.set_zero();
}
ZerostateInfo(std::string _filename) : filename(_filename) {
file_hash.set_zero();
root_hash.set_zero();
}
ZerostateInfo(RootHash hash, FileHash fhash) : file_hash(std::move(fhash)), root_hash(std::move(hash)) {
}
bool has_file_hash() const {
return !file_hash.is_zero();
}
bool has_root_hash() const {
return !root_hash.is_zero();
}
bool has_filename() const {
return !filename.empty();
}
bool has_data() const {
return !data.empty();
}
td::Status base_check();
};
enum class FileType {
unknown = 0,
unknown_boc = 1,
block = 2,
block_candidate = 3,
collated_data = 4,
block_signatures = 5,
state = 6,
out_queue = 7
};
struct FileInfo : public td::CntObject {
ton::BlockIdExt blk;
FileType type;
int status;
long long file_size;
td::BufferSlice data;
FileInfo() : type(FileType::unknown), status(0), file_size(-1) {
blk.file_hash.set_zero();
blk.root_hash.set_zero();
}
FileInfo(FileType _type, const ton::BlockId& _id, int _status, const FileHash& _fhash, long long _fsize = -1)
: blk(_id, _fhash), type(_type), status(_status), file_size(_fsize) {
blk.root_hash.set_zero();
}
FileInfo(FileType _type, const ton::BlockId& _id, int _status, const FileHash& _fhash, const RootHash& _rhash,
long long _fsize = -1)
: blk(_id, _rhash, _fhash), type(_type), status(_status), file_size(_fsize) {
}
FileInfo(FileType _type, const ton::BlockId& _id, int _status, const FileHash& _fhash, const RootHash& _rhash,
td::BufferSlice _data)
: blk(_id, _rhash, _fhash), type(_type), status(_status), file_size(_data.size()), data(std::move(_data)) {
}
FileInfo(FileInfo&& other) = default;
FileInfo clone() const;
FileInfo* make_copy() const override;
private:
FileInfo(const FileInfo& other);
};
struct OutputQueueInfoDescr : public td::CntObject {
ton::BlockId id;
RootHash block_hash;
RootHash state_hash;
RootHash output_queue_info_hash;
td::Ref<vm::Cell> queue_info;
OutputQueueInfoDescr(ton::BlockId _id, const RootHash& _bhash, const RootHash& _shash, Ref<vm::Cell> _qinfo)
: id(_id)
, block_hash(_bhash)
, state_hash(_shash)
, output_queue_info_hash(_qinfo->get_hash().bits())
, queue_info(std::move(_qinfo)) {
}
OutputQueueInfoDescr(ton::BlockId _id, td::ConstBitPtr _bhash, td::ConstBitPtr _shash, Ref<vm::Cell> _qinfo)
: id(_id)
, block_hash(_bhash)
, state_hash(_shash)
, output_queue_info_hash(_qinfo->get_hash().bits())
, queue_info(std::move(_qinfo)) {
}
};
class BlockDb : public td::actor::Actor {
public:
BlockDb() = default;
virtual ~BlockDb() = default;
static td::Result<td::actor::ActorOwn<BlockDb>> create_block_db(std::string _base_dir,
std::unique_ptr<ZerostateInfo> _zstate = nullptr,
bool _allow_uninit = false, int _depth = 4,
std::string _binlog_name = "");
// authority: 0 = standard (inclusion in mc block), 1 = validator (by 2/3 validator signatures)
virtual void get_top_block_id(ton::ShardIdFull shard, int authority, td::Promise<ton::BlockIdExt> promise) = 0;
virtual void get_top_block_state_id(ton::ShardIdFull shard, int authority, td::Promise<ton::BlockIdExt> promise) = 0;
virtual void get_block_by_id(ton::BlockId blk_id, bool need_data, td::Promise<td::Ref<FileInfo>> promise) = 0;
virtual void get_state_by_id(ton::BlockId blk_id, bool need_data, td::Promise<td::Ref<FileInfo>> promise) = 0;
virtual void get_out_queue_info_by_id(ton::BlockId blk_id, td::Promise<td::Ref<OutputQueueInfoDescr>> promise) = 0;
virtual void get_object_by_file_hash(FileHash file_hash, bool need_data, bool force_file_load,
td::Promise<td::Ref<FileInfo>> promise) = 0;
virtual void get_object_by_root_hash(RootHash root_hash, bool need_data, bool force_file_load,
td::Promise<td::Ref<FileInfo>> promise) = 0;
virtual void save_new_block(ton::BlockIdExt blk_id, td::BufferSlice data, int authority,
td::Promise<td::Unit> promise) = 0;
virtual void save_new_state(ton::BlockIdExt state_id, td::BufferSlice data, int authority,
td::Promise<td::Unit> promise) = 0;
};
bool parse_hash_string(std::string arg, RootHash& res);
FileHash compute_file_hash(const td::BufferSlice& data);
FileHash compute_file_hash(td::Slice data);
td::Result<td::BufferSlice> load_binary_file(std::string filename, td::int64 max_size = 0);
td::Status save_binary_file(std::string filename, const td::BufferSlice& data, unsigned long long max_size = 0);
std::string compute_db_filename(std::string base_dir, const FileHash& file_hash, int depth = 4);
std::string compute_db_tmp_filename(std::string base_dir, const FileHash& file_hash, int i = 0, bool makedirs = true,
int depth = 4);
} // namespace block

2135
crypto/block/block-parse.cpp Normal file

File diff suppressed because it is too large Load diff

1081
crypto/block/block-parse.h Normal file

File diff suppressed because it is too large Load diff

1923
crypto/block/block.cpp Normal file

File diff suppressed because it is too large Load diff

569
crypto/block/block.h Normal file
View file

@ -0,0 +1,569 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "common/refcnt.hpp"
#include "vm/cells.h"
#include "vm/cellslice.h"
#include "vm/dict.h"
#include "vm/boc.h"
#include "vm/stack.hpp"
#include <ostream>
#include "tl/tlblib.hpp"
#include "td/utils/bits.h"
#include "td/utils/StringBuilder.h"
#include "ton/ton-types.h"
namespace block {
using td::Ref;
struct StdAddress {
ton::WorkchainId workchain{ton::workchainInvalid};
bool bounceable{true}; // addresses must be bounceable by default
bool testnet{false};
ton::StdSmcAddress addr;
StdAddress() = default;
StdAddress(ton::WorkchainId _wc, const ton::StdSmcAddress& _addr, bool _bounce = true, bool _testnet = false)
: workchain(_wc), bounceable(_bounce), testnet(_testnet), addr(_addr) {
}
StdAddress(ton::WorkchainId _wc, td::ConstBitPtr _addr, bool _bounce = true, bool _testnet = false)
: workchain(_wc), bounceable(_bounce), testnet(_testnet), addr(_addr) {
}
explicit StdAddress(std::string serialized);
explicit StdAddress(td::Slice from);
bool is_valid() const {
return workchain != ton::workchainInvalid;
}
bool invalidate() {
workchain = ton::workchainInvalid;
return false;
}
std::string rserialize(bool base64_url = false) const;
bool rserialize_to(td::MutableSlice to, bool base64_url = false) const;
bool rserialize_to(char to[48], bool base64_url = false) const;
bool rdeserialize(td::Slice from);
bool rdeserialize(std::string from);
bool rdeserialize(const char from[48]);
bool parse_addr(td::Slice acc_string);
bool operator==(const StdAddress& other) const;
static td::Result<StdAddress> parse(td::Slice acc_string);
};
inline td::StringBuilder& operator<<(td::StringBuilder& sb, const StdAddress& addr) {
return sb << addr.workchain << " : " << addr.addr.to_hex();
}
bool parse_std_account_addr(td::Slice acc_string, ton::WorkchainId& wc, ton::StdSmcAddress& addr,
bool* bounceable = nullptr, bool* testnet_only = nullptr);
struct ShardId {
ton::WorkchainId workchain_id;
int shard_pfx_len;
unsigned long long shard_pfx;
ShardId(ton::WorkchainId wc_id = ton::workchainInvalid)
: workchain_id(wc_id), shard_pfx_len(0), shard_pfx(1ULL << 63) {
}
ShardId(ton::WorkchainId wc_id, unsigned long long sh_pfx);
ShardId(ton::ShardIdFull ton_shard);
ShardId(ton::BlockId ton_block);
ShardId(const ton::BlockIdExt& ton_block);
ShardId(ton::WorkchainId wc_id, unsigned long long sh_pfx, int sh_pfx_len);
ShardId(vm::CellSlice& cs) {
deserialize(cs);
}
ShardId(Ref<vm::CellSlice> cs_ref) {
vm::CellSlice cs{*cs_ref};
deserialize(cs);
}
explicit operator ton::ShardIdFull() const {
return ton::ShardIdFull{workchain_id, shard_pfx};
}
bool operator==(const ShardId& other) const {
return workchain_id == other.workchain_id && shard_pfx == other.shard_pfx;
}
void invalidate() {
workchain_id = ton::workchainInvalid;
shard_pfx_len = 0;
}
bool is_valid() const {
return workchain_id != ton::workchainInvalid;
}
void show(std::ostream& os) const;
std::string to_str() const;
bool serialize(vm::CellBuilder& cb) const;
bool deserialize(vm::CellSlice& cs);
private:
void init();
};
struct EnqueuedMsgDescr {
ton::AccountIdPrefixFull src_prefix_, cur_prefix_, next_prefix_, dest_prefix_;
ton::LogicalTime lt_;
ton::LogicalTime enqueued_lt_;
ton::Bits256 hash_;
Ref<vm::Cell> msg_;
Ref<vm::Cell> msg_env_;
EnqueuedMsgDescr() = default;
EnqueuedMsgDescr(ton::AccountIdPrefixFull cur_pfx, ton::AccountIdPrefixFull next_pfx, ton::LogicalTime lt,
ton::LogicalTime enqueued_lt, td::ConstBitPtr hash)
: cur_prefix_(cur_pfx), next_prefix_(next_pfx), lt_(lt), enqueued_lt_(enqueued_lt), hash_(hash) {
}
bool is_valid() const {
return next_prefix_.is_valid();
}
bool check_key(td::ConstBitPtr key) const;
bool invalidate() {
next_prefix_.workchain = cur_prefix_.workchain = ton::workchainInvalid;
return false;
}
bool unpack(vm::CellSlice& cs);
};
using compute_shard_end_lt_func_t = std::function<ton::LogicalTime(ton::AccountIdPrefixFull)>;
struct MsgProcessedUpto {
ton::ShardId shard;
ton::BlockSeqno mc_seqno;
ton::LogicalTime last_inmsg_lt;
ton::Bits256 last_inmsg_hash;
compute_shard_end_lt_func_t compute_shard_end_lt;
MsgProcessedUpto() = default;
MsgProcessedUpto(ton::ShardId _shard, ton::BlockSeqno _mcseqno, ton::LogicalTime _lt, td::ConstBitPtr _hash)
: shard(_shard), mc_seqno(_mcseqno), last_inmsg_lt(_lt), last_inmsg_hash(_hash) {
}
bool operator<(const MsgProcessedUpto& other) const & {
return shard < other.shard || (shard == other.shard && mc_seqno < other.mc_seqno);
}
bool contains(const MsgProcessedUpto& other) const &;
bool contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash,
ton::BlockSeqno other_mc_seqno) const &;
// NB: this is for checking whether we have already imported an internal message
bool already_processed(const EnqueuedMsgDescr& msg) const;
};
struct MsgProcessedUptoCollection {
ton::ShardIdFull owner;
bool valid{false};
std::vector<MsgProcessedUpto> list;
MsgProcessedUptoCollection(ton::ShardIdFull _owner) : owner(_owner) {
}
MsgProcessedUptoCollection(ton::ShardIdFull _owner, Ref<vm::CellSlice> cs_ref);
static std::unique_ptr<MsgProcessedUptoCollection> unpack(ton::ShardIdFull _owner, Ref<vm::CellSlice> cs_ref);
bool is_valid() const {
return valid;
}
bool insert(ton::BlockSeqno mc_seqno, ton::LogicalTime last_proc_lt, td::ConstBitPtr last_proc_hash);
bool insert_infty(ton::BlockSeqno mc_seqno, ton::LogicalTime last_proc_lt = ~0ULL);
bool compactify();
bool pack(vm::CellBuilder& cb);
bool is_reduced() const;
bool contains(const MsgProcessedUpto& other) const;
bool contains(const MsgProcessedUptoCollection& other) const;
const MsgProcessedUpto* is_simple_update_of(const MsgProcessedUptoCollection& other, bool& ok) const;
ton::BlockSeqno min_mc_seqno() const;
bool split(ton::ShardIdFull new_owner);
bool combine_with(const MsgProcessedUptoCollection& other);
// NB: this is for checking whether we have already imported an internal message
bool already_processed(const EnqueuedMsgDescr& msg) const;
bool for_each_mcseqno(std::function<bool(ton::BlockSeqno)>) const;
};
struct ParamLimits {
enum { limits_cnt = 4 };
enum { cl_underload = 0, cl_normal = 1, cl_soft = 2, cl_medium = 3, cl_hard = 4 };
ParamLimits() = default;
ParamLimits(td::uint32 underload, td::uint32 soft_lim, td::uint32 hard_lim)
: limits_{underload, soft_lim, (soft_lim + hard_lim) / 2, hard_lim} {
}
td::uint32 underload() const {
return limits_[0];
}
td::uint32 soft() const {
return limits_[1];
}
td::uint32 hard() const {
return limits_[3];
}
bool compute_medium_limit() {
limits_[2] = soft() + ((hard() - soft()) >> 1);
return true;
}
bool deserialize(vm::CellSlice& cs);
int classify(td::uint64 value) const;
bool fits(unsigned cls, td::uint64 value) const;
private:
std::array<td::uint32, limits_cnt> limits_;
};
struct BlockLimits {
ParamLimits bytes, gas, lt_delta;
ton::LogicalTime start_lt{0};
const vm::CellUsageTree* usage_tree{nullptr};
bool deserialize(vm::CellSlice& cs);
int classify_size(td::uint64 size) const;
int classify_gas(td::uint64 gas) const;
int classify_lt(ton::LogicalTime lt) const;
int classify(td::uint64 size, td::uint64 gas, ton::LogicalTime lt) const;
bool fits(unsigned cls, td::uint64 size, td::uint64 gas, ton::LogicalTime lt) const;
};
struct BlockLimitStatus {
const BlockLimits& limits;
ton::LogicalTime cur_lt;
td::uint64 gas_used{};
vm::NewCellStorageStat st_stat;
unsigned accounts{}, transactions{};
BlockLimitStatus(const BlockLimits& limits_, ton::LogicalTime lt = 0)
: limits(limits_), cur_lt(std::max(limits_.start_lt, lt)) {
}
void reset() {
cur_lt = limits.start_lt;
st_stat.set_zero();
transactions = accounts = 0;
gas_used = 0;
}
td::uint64 estimate_block_size(const vm::NewCellStorageStat::Stat* extra = nullptr) const;
int classify() const;
bool fits(unsigned cls) const;
bool would_fit(unsigned cls, ton::LogicalTime end_lt, td::uint64 more_gas,
const vm::NewCellStorageStat::Stat* extra = nullptr) const;
bool add_cell(Ref<vm::Cell> cell) {
st_stat.add_cell(std::move(cell));
return true;
}
bool add_proof(Ref<vm::Cell> cell) {
st_stat.add_proof(std::move(cell), limits.usage_tree);
return true;
}
bool update_lt(ton::LogicalTime lt) {
cur_lt = std::max(lt, cur_lt);
return true;
}
bool update_gas(td::uint64 more_gas) {
gas_used += more_gas;
return true;
}
bool add_transaction(unsigned cnt = 1) {
transactions += cnt;
return true;
}
bool add_account(unsigned cnt = 1) {
accounts += cnt;
return true;
}
};
namespace tlb {
struct CurrencyCollection;
} // namespace tlb
struct CurrencyCollection {
using type_class = block::tlb::CurrencyCollection;
td::RefInt256 grams;
Ref<vm::Cell> extra;
CurrencyCollection() = default;
explicit CurrencyCollection(td::RefInt256 _grams, Ref<vm::Cell> _extra = {})
: grams(std::move(_grams)), extra(std::move(_extra)) {
}
explicit CurrencyCollection(long long _grams, Ref<vm::Cell> _extra = {})
: grams(true, _grams), extra(std::move(_extra)) {
}
bool set_zero() {
grams = td::RefInt256{true, 0};
extra.clear();
return true;
}
static CurrencyCollection zero() {
return CurrencyCollection(td::RefInt256{true, 0});
}
bool is_valid() const {
return grams.not_null();
}
bool is_zero() const {
return is_valid() && extra.is_null() && !td::sgn(grams);
}
bool has_extra() const {
return extra.not_null();
}
bool invalidate() {
extra.clear();
grams.clear();
return false;
}
bool validate() const;
bool validate_extra() const;
bool operator==(const CurrencyCollection& other) const;
bool operator!=(const CurrencyCollection& other) const {
return !operator==(other);
}
bool operator==(td::RefInt256 other_grams) const {
return is_valid() && !has_extra() && !td::cmp(grams, other_grams);
}
bool operator!=(td::RefInt256 other_grams) const {
return !operator==(std::move(other_grams));
}
bool operator>=(const CurrencyCollection& other) const;
bool operator<=(const CurrencyCollection& other) const {
return other >= *this;
}
static bool add(const CurrencyCollection& a, const CurrencyCollection& b, CurrencyCollection& c);
static bool add(const CurrencyCollection& a, CurrencyCollection&& b, CurrencyCollection& c);
CurrencyCollection& operator+=(const CurrencyCollection& other);
CurrencyCollection& operator+=(CurrencyCollection&& other);
CurrencyCollection& operator+=(td::RefInt256 other_grams);
CurrencyCollection operator+(const CurrencyCollection& other) const;
CurrencyCollection operator+(CurrencyCollection&& other) const;
CurrencyCollection operator+(td::RefInt256 other_grams);
static bool sub(const CurrencyCollection& a, const CurrencyCollection& b, CurrencyCollection& c);
static bool sub(const CurrencyCollection& a, CurrencyCollection&& b, CurrencyCollection& c);
CurrencyCollection& operator-=(const CurrencyCollection& other);
CurrencyCollection& operator-=(CurrencyCollection&& other);
CurrencyCollection& operator-=(td::RefInt256 other_grams);
CurrencyCollection operator-(const CurrencyCollection& other) const;
CurrencyCollection operator-(CurrencyCollection&& other) const;
CurrencyCollection operator-(td::RefInt256 other_grams) const;
bool store(vm::CellBuilder& cb) const;
bool store_or_zero(vm::CellBuilder& cb) const;
bool fetch(vm::CellSlice& cs);
bool fetch_exact(vm::CellSlice& cs);
bool unpack(Ref<vm::CellSlice> csr);
bool validate_unpack(Ref<vm::CellSlice> csr);
Ref<vm::CellSlice> pack() const;
bool pack_to(Ref<vm::CellSlice>& csr) const {
return (csr = pack()).not_null();
}
Ref<vm::Tuple> as_vm_tuple() const {
if (is_valid()) {
return vm::make_tuple_ref(grams, vm::StackEntry::maybe(extra));
} else {
return {};
}
}
bool show(std::ostream& os) const;
std::string to_str() const;
};
std::ostream& operator<<(std::ostream& os, const CurrencyCollection& cc);
struct ShardState {
enum { verbosity = 0 };
ton::BlockIdExt id_;
Ref<vm::Cell> root_;
int global_id_;
ton::UnixTime utime_;
ton::LogicalTime lt_;
ton::BlockSeqno mc_blk_seqno_, min_ref_mc_seqno_;
ton::BlockIdExt mc_blk_ref_;
ton::LogicalTime mc_blk_lt_;
bool before_split_{false};
std::unique_ptr<vm::AugmentedDictionary> account_dict_;
std::unique_ptr<vm::Dictionary> shard_libraries_;
Ref<vm::Cell> mc_state_extra_;
td::uint64 overload_history_{0}, underload_history_{0};
CurrencyCollection total_balance_, total_validator_fees_, global_balance_;
std::unique_ptr<vm::AugmentedDictionary> out_msg_queue_;
std::unique_ptr<vm::Dictionary> ihr_pending_;
std::shared_ptr<block::MsgProcessedUptoCollection> processed_upto_;
bool is_valid() const {
return id_.is_valid();
}
bool is_masterchain() const {
return id_.is_masterchain();
}
bool invalidate() {
id_.invalidate();
return false;
}
td::Status unpack_state(ton::BlockIdExt id, Ref<vm::Cell> state_root);
td::Status unpack_state_ext(ton::BlockIdExt id, Ref<vm::Cell> state_root, int global_id,
ton::BlockSeqno prev_mc_block_seqno, bool after_split, bool clear_history,
std::function<bool(ton::BlockSeqno)> for_each_mcseqno);
td::Status merge_with(ShardState& sib);
td::Result<std::unique_ptr<vm::AugmentedDictionary>> compute_split_out_msg_queue(ton::ShardIdFull subshard);
td::Result<std::shared_ptr<block::MsgProcessedUptoCollection>> compute_split_processed_upto(
ton::ShardIdFull subshard);
td::Status split(ton::ShardIdFull subshard);
td::Status unpack_out_msg_queue_info(Ref<vm::Cell> out_msg_queue_info);
bool clear_load_history() {
overload_history_ = underload_history_ = 0;
return true;
}
bool clear_load_history_if(bool cond) {
return !cond || clear_load_history();
}
td::Status check_before_split(bool before_split) const;
td::Status check_global_id(int req_global_id) const;
td::Status check_mc_blk_seqno(ton::BlockSeqno last_mc_block_seqno) const;
bool update_prev_utime_lt(ton::UnixTime& prev_utime, ton::LogicalTime& prev_lt) const;
bool for_each_mcseqno(std::function<bool(ton::BlockSeqno)> func) const {
return processed_upto_ && processed_upto_->for_each_mcseqno(std::move(func));
}
};
struct ValueFlow {
struct SetZero {};
CurrencyCollection from_prev_blk, to_next_blk, imported, exported, fees_collected, fees_imported, recovered, created,
minted;
ValueFlow() = default;
ValueFlow(SetZero)
: from_prev_blk{0}
, to_next_blk{0}
, imported{0}
, exported{0}
, fees_collected{0}
, fees_imported{0}
, recovered{0}
, created{0}
, minted{0} {
}
bool is_valid() const {
return from_prev_blk.is_valid() && minted.is_valid();
}
bool validate() const;
bool invalidate() {
return from_prev_blk.invalidate();
}
bool set_zero();
bool store(vm::CellBuilder& cb) const;
bool fetch(vm::CellSlice& cs);
bool unpack(Ref<vm::CellSlice> csr);
bool show(std::ostream& os) const;
std::string to_str() const;
private:
bool show_one(std::ostream& os, const char* str, const CurrencyCollection& cc) const;
};
std::ostream& operator<<(std::ostream& os, const ValueFlow& vflow);
struct BlkProofLink {
ton::BlockIdExt from, to;
bool is_key{false}, is_fwd{false};
Ref<vm::Cell> dest_proof, shard_proof, proof;
ton::CatchainSeqno cc_seqno{0};
td::uint32 validator_set_hash{0};
std::vector<ton::BlockSignature> signatures;
BlkProofLink(ton::BlockIdExt _from, ton::BlockIdExt _to, bool _iskey = false)
: from(_from), to(_to), is_key(_iskey), is_fwd(to.seqno() > from.seqno()) {
}
};
struct BlkProofChain {
ton::BlockIdExt from, to;
int mode;
std::vector<BlkProofLink> links;
std::size_t link_count() const {
return links.size();
}
BlkProofChain(ton::BlockIdExt _from, ton::BlockIdExt _to, int _mode) : from(_from), to(_to), mode(_mode) {
}
};
int filter_out_msg_queue(vm::AugmentedDictionary& out_queue, ton::ShardIdFull old_shard, ton::ShardIdFull subshard);
std::ostream& operator<<(std::ostream& os, const ShardId& shard_id);
bool pack_std_smc_addr_to(char result[48], bool base64_url, ton::WorkchainId wc, const ton::StdSmcAddress& addr,
bool bounceable, bool testnet);
std::string pack_std_smc_addr(bool base64_url, ton::WorkchainId wc, const ton::StdSmcAddress& addr, bool bounceable,
bool testnet);
bool unpack_std_smc_addr(const char packed[48], ton::WorkchainId& wc, ton::StdSmcAddress& addr, bool& bounceable,
bool& testnet);
bool unpack_std_smc_addr(td::Slice packed, ton::WorkchainId& wc, ton::StdSmcAddress& addr, bool& bounceable,
bool& testnet);
bool unpack_std_smc_addr(std::string packed, ton::WorkchainId& wc, ton::StdSmcAddress& addr, bool& bounceable,
bool& testnet);
bool store_UInt7(vm::CellBuilder& cb, unsigned long long value);
bool store_UInt7(vm::CellBuilder& cb, unsigned long long value1, unsigned long long value2);
bool store_Maybe_Grams(vm::CellBuilder& cb, td::RefInt256 value);
bool store_Maybe_Grams_nz(vm::CellBuilder& cb, td::RefInt256 value);
bool store_CurrencyCollection(vm::CellBuilder& cb, td::RefInt256 value, Ref<vm::Cell> extra);
bool fetch_CurrencyCollection(vm::CellSlice& cs, td::RefInt256& value, Ref<vm::Cell>& extra, bool inexact = false);
bool unpack_CurrencyCollection(Ref<vm::CellSlice> csr, td::RefInt256& value, Ref<vm::Cell>& extra);
bool valid_library_collection(Ref<vm::Cell> cell, bool catch_errors = true);
bool valid_config_data(Ref<vm::Cell> cell, const td::BitArray<256>& addr, bool catch_errors = true,
bool relax_par0 = false);
bool add_extra_currency(Ref<vm::Cell> extra1, Ref<vm::Cell> extra2, Ref<vm::Cell>& res);
bool sub_extra_currency(Ref<vm::Cell> extra1, Ref<vm::Cell> extra2, Ref<vm::Cell>& res);
ton::AccountIdPrefixFull interpolate_addr(const ton::AccountIdPrefixFull& src, const ton::AccountIdPrefixFull& dest,
int used_dest_bits);
bool interpolate_addr_to(const ton::AccountIdPrefixFull& src, const ton::AccountIdPrefixFull& dest, int used_dest_bits,
ton::AccountIdPrefixFull& res);
// result: (transit_addr_dest_bits, nh_addr_dest_bits)
std::pair<int, int> perform_hypercube_routing(ton::AccountIdPrefixFull src, ton::AccountIdPrefixFull dest,
ton::ShardIdFull cur, int used_dest_bits = 0);
bool compute_out_msg_queue_key(Ref<vm::Cell> msg_env, td::BitArray<352>& key);
bool unpack_block_prev_blk(Ref<vm::Cell> block_root, const ton::BlockIdExt& id, std::vector<ton::BlockIdExt>& prev,
ton::BlockIdExt& mc_blkid, bool& after_split, ton::BlockIdExt* fetch_blkid = nullptr);
td::Status unpack_block_prev_blk_ext(Ref<vm::Cell> block_root, const ton::BlockIdExt& id,
std::vector<ton::BlockIdExt>& prev, ton::BlockIdExt& mc_blkid, bool& after_split,
ton::BlockIdExt* fetch_blkid = nullptr);
td::Status unpack_block_prev_blk_try(Ref<vm::Cell> block_root, const ton::BlockIdExt& id,
std::vector<ton::BlockIdExt>& prev, ton::BlockIdExt& mc_blkid, bool& after_split,
ton::BlockIdExt* fetch_blkid = nullptr);
std::unique_ptr<vm::AugmentedDictionary> get_prev_blocks_dict(Ref<vm::Cell> state_root);
bool get_old_mc_block_id(vm::AugmentedDictionary* prev_blocks_dict, ton::BlockSeqno seqno, ton::BlockIdExt& blkid,
ton::LogicalTime* end_lt = nullptr);
bool get_old_mc_block_id(vm::AugmentedDictionary& prev_blocks_dict, ton::BlockSeqno seqno, ton::BlockIdExt& blkid,
ton::LogicalTime* end_lt = nullptr);
bool unpack_old_mc_block_id(Ref<vm::CellSlice> old_blk_info, ton::BlockSeqno seqno, ton::BlockIdExt& blkid,
ton::LogicalTime* end_lt = nullptr);
bool check_old_mc_block_id(vm::AugmentedDictionary* prev_blocks_dict, const ton::BlockIdExt& blkid);
bool check_old_mc_block_id(vm::AugmentedDictionary& prev_blocks_dict, const ton::BlockIdExt& blkid);
td::Result<Ref<vm::Cell>> get_block_transaction(Ref<vm::Cell> block_root, ton::WorkchainId workchain,
const ton::StdSmcAddress& addr, ton::LogicalTime lt);
td::Result<Ref<vm::Cell>> get_block_transaction_try(Ref<vm::Cell> block_root, ton::WorkchainId workchain,
const ton::StdSmcAddress& addr, ton::LogicalTime lt);
bool get_transaction_in_msg(Ref<vm::Cell> trans_ref, Ref<vm::Cell>& in_msg);
bool is_transaction_in_msg(Ref<vm::Cell> trans_ref, Ref<vm::Cell> msg);
bool is_transaction_out_msg(Ref<vm::Cell> trans_ref, Ref<vm::Cell> msg);
bool get_transaction_id(Ref<vm::Cell> trans_ref, ton::StdSmcAddress& account_addr, ton::LogicalTime& lt);
bool get_transaction_owner(Ref<vm::Cell> trans_ref, ton::StdSmcAddress& addr);
td::uint32 compute_validator_set_hash(ton::CatchainSeqno cc_seqno, ton::ShardIdFull from,
const std::vector<ton::ValidatorDescr>& nodes);
td::Result<Ref<vm::Cell>> get_config_data_from_smc(Ref<vm::Cell> acc_root);
td::Result<Ref<vm::Cell>> get_config_data_from_smc(Ref<vm::CellSlice> acc_csr);
bool important_config_parameters_changed(Ref<vm::Cell> old_cfg_root, Ref<vm::Cell> new_cfg_root, bool coarse = false);
bool is_public_library(td::ConstBitPtr key, Ref<vm::CellSlice> val);
bool parse_hex_hash(const char* str, const char* end, td::Bits256& hash);
bool parse_hex_hash(td::Slice str, td::Bits256& hash);
bool parse_block_id_ext(const char* str, const char* end, ton::BlockIdExt& blkid);
bool parse_block_id_ext(td::Slice str, ton::BlockIdExt& blkid);
} // namespace block

662
crypto/block/block.tlb Normal file
View file

@ -0,0 +1,662 @@
unit$_ = Unit;
true$_ = True;
// EMPTY False;
bool_false$0 = Bool;
bool_true$1 = Bool;
bool_false$0 = BoolFalse;
bool_true$1 = BoolTrue;
nothing$0 {X:Type} = Maybe X;
just$1 {X:Type} value:X = Maybe X;
left$0 {X:Type} {Y:Type} value:X = Either X Y;
right$1 {X:Type} {Y:Type} value:Y = Either X Y;
pair$_ {X:Type} {Y:Type} first:X second:Y = Both X Y;
bit$_ (## 1) = Bit;
/*
*
* FROM hashmap.tlb
*
*/
// ordinary Hashmap / HashmapE, with fixed length keys
//
hm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l n)
{n = (~m) + l} node:(HashmapNode m X) = Hashmap n X;
hmn_leaf#_ {X:Type} value:X = HashmapNode 0 X;
hmn_fork#_ {n:#} {X:Type} left:^(Hashmap n X)
right:^(Hashmap n X) = HashmapNode (n + 1) X;
hml_short$0 {m:#} {n:#} len:(Unary ~n) {n <= m} s:(n * Bit) = HmLabel ~n m;
hml_long$10 {m:#} n:(#<= m) s:(n * Bit) = HmLabel ~n m;
hml_same$11 {m:#} v:Bit n:(#<= m) = HmLabel ~n m;
unary_zero$0 = Unary ~0;
unary_succ$1 {n:#} x:(Unary ~n) = Unary ~(n + 1);
hme_empty$0 {n:#} {X:Type} = HashmapE n X;
hme_root$1 {n:#} {X:Type} root:^(Hashmap n X) = HashmapE n X;
// true#_ = True;
_ {n:#} _:(Hashmap n True) = BitstringSet n;
// HashmapAug, hashmap with an extra value
// (augmentation) of type Y at every node
//
ahm_edge#_ {n:#} {X:Type} {Y:Type} {l:#} {m:#}
label:(HmLabel ~l n) {n = (~m) + l}
node:(HashmapAugNode m X Y) = HashmapAug n X Y;
ahmn_leaf#_ {X:Type} {Y:Type} extra:Y value:X = HashmapAugNode 0 X Y;
ahmn_fork#_ {n:#} {X:Type} {Y:Type} left:^(HashmapAug n X Y)
right:^(HashmapAug n X Y) extra:Y = HashmapAugNode (n + 1) X Y;
ahme_empty$0 {n:#} {X:Type} {Y:Type} extra:Y
= HashmapAugE n X Y;
ahme_root$1 {n:#} {X:Type} {Y:Type} root:^(HashmapAug n X Y)
extra:Y = HashmapAugE n X Y;
// VarHashmap / VarHashmapE, with variable-length keys
//
vhm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l n)
{n = (~m) + l} node:(VarHashmapNode m X)
= VarHashmap n X;
vhmn_leaf$00 {n:#} {X:Type} value:X = VarHashmapNode n X;
vhmn_fork$01 {n:#} {X:Type} left:^(VarHashmap n X)
right:^(VarHashmap n X) value:(Maybe X)
= VarHashmapNode (n + 1) X;
vhmn_cont$1 {n:#} {X:Type} branch:Bit child:^(VarHashmap n X)
value:X = VarHashmapNode (n + 1) X;
// nothing$0 {X:Type} = Maybe X;
// just$1 {X:Type} value:X = Maybe X;
vhme_empty$0 {n:#} {X:Type} = VarHashmapE n X;
vhme_root$1 {n:#} {X:Type} root:^(VarHashmap n X)
= VarHashmapE n X;
//
// PfxHashmap / PfxHashmapE, with variable-length keys
// constituting a prefix code
//
phm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l n)
{n = (~m) + l} node:(PfxHashmapNode m X)
= PfxHashmap n X;
phmn_leaf$0 {n:#} {X:Type} value:X = PfxHashmapNode n X;
phmn_fork$1 {n:#} {X:Type} left:^(PfxHashmap n X)
right:^(PfxHashmap n X) = PfxHashmapNode (n + 1) X;
phme_empty$0 {n:#} {X:Type} = PfxHashmapE n X;
phme_root$1 {n:#} {X:Type} root:^(PfxHashmap n X)
= PfxHashmapE n X;
/*
*
* END hashmap.tlb
*
*/
//
// TON BLOCK LAYOUT
//
addr_none$00 = MsgAddressExt;
addr_extern$01 len:(## 9) external_address:(bits len)
= MsgAddressExt;
anycast_info$_ depth:(#<= 30) { depth >= 1 }
rewrite_pfx:(bits depth) = Anycast;
addr_std$10 anycast:(Maybe Anycast)
workchain_id:int8 address:bits256 = MsgAddressInt;
addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9)
workchain_id:int32 address:(bits addr_len) = MsgAddressInt;
_ _:MsgAddressInt = MsgAddress;
_ _:MsgAddressExt = MsgAddress;
//
var_uint$_ {n:#} len:(#< n) value:(uint (len * 8))
= VarUInteger n;
var_int$_ {n:#} len:(#< n) value:(int (len * 8))
= VarInteger n;
nanograms$_ amount:(VarUInteger 16) = Grams;
//
extra_currencies$_ dict:(HashmapE 32 (VarUInteger 32))
= ExtraCurrencyCollection;
currencies$_ grams:Grams other:ExtraCurrencyCollection
= CurrencyCollection;
//
int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
src:MsgAddressInt dest:MsgAddressInt
value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
created_lt:uint64 created_at:uint32 = CommonMsgInfo;
ext_in_msg_info$10 src:MsgAddressExt dest:MsgAddressInt
import_fee:Grams = CommonMsgInfo;
ext_out_msg_info$11 src:MsgAddressInt dest:MsgAddressExt
created_lt:uint64 created_at:uint32 = CommonMsgInfo;
int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
src:MsgAddress dest:MsgAddressInt
value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;
ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt
created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;
tick_tock$_ tick:Bool tock:Bool = TickTock;
_ split_depth:(Maybe (## 5)) special:(Maybe TickTock)
code:(Maybe ^Cell) data:(Maybe ^Cell)
library:(HashmapE 256 SimpleLib) = StateInit;
simple_lib$_ public:Bool root:^Cell = SimpleLib;
message$_ {X:Type} info:CommonMsgInfo
init:(Maybe (Either StateInit ^StateInit))
body:(Either X ^X) = Message X;
message$_ {X:Type} info:CommonMsgInfoRelaxed
init:(Maybe (Either StateInit ^StateInit))
body:(Either X ^X) = MessageRelaxed X;
//
interm_addr_regular$0 use_dest_bits:(#<= 96)
= IntermediateAddress;
interm_addr_simple$10 workchain_id:int8 addr_pfx:uint64
= IntermediateAddress;
interm_addr_ext$11 workchain_id:int32 addr_pfx:uint64
= IntermediateAddress;
msg_envelope#4 cur_addr:IntermediateAddress
next_addr:IntermediateAddress fwd_fee_remaining:Grams
msg:^(Message Any) = MsgEnvelope;
//
msg_import_ext$000 msg:^(Message Any) transaction:^Transaction
= InMsg;
msg_import_ihr$010 msg:^(Message Any) transaction:^Transaction
ihr_fee:Grams proof_created:^Cell = InMsg;
msg_import_imm$011 in_msg:^MsgEnvelope
transaction:^Transaction fwd_fee:Grams = InMsg;
msg_import_fin$100 in_msg:^MsgEnvelope
transaction:^Transaction fwd_fee:Grams = InMsg;
msg_import_tr$101 in_msg:^MsgEnvelope out_msg:^MsgEnvelope
transit_fee:Grams = InMsg;
msg_discard_fin$110 in_msg:^MsgEnvelope transaction_id:uint64
fwd_fee:Grams = InMsg;
msg_discard_tr$111 in_msg:^MsgEnvelope transaction_id:uint64
fwd_fee:Grams proof_delivered:^Cell = InMsg;
//
import_fees$_ fees_collected:Grams
value_imported:CurrencyCollection = ImportFees;
_ (HashmapAugE 256 InMsg ImportFees) = InMsgDescr;
msg_export_ext$000 msg:^(Message Any)
transaction:^Transaction = OutMsg;
msg_export_imm$010 out_msg:^MsgEnvelope
transaction:^Transaction reimport:^InMsg = OutMsg;
msg_export_new$001 out_msg:^MsgEnvelope
transaction:^Transaction = OutMsg;
msg_export_tr$011 out_msg:^MsgEnvelope
imported:^InMsg = OutMsg;
msg_export_deq$110 out_msg:^MsgEnvelope // out_msg_hash:bits256 ?
import_block_lt:uint64 = OutMsg;
msg_export_tr_req$111 out_msg:^MsgEnvelope
imported:^InMsg = OutMsg;
msg_export_deq_imm$100 out_msg:^MsgEnvelope
reimport:^InMsg = OutMsg;
_ enqueued_lt:uint64 out_msg:^MsgEnvelope = EnqueuedMsg;
_ (HashmapAugE 256 OutMsg CurrencyCollection) = OutMsgDescr;
_ (HashmapAugE 352 EnqueuedMsg uint64) = OutMsgQueue;
processed_upto$_ last_msg_lt:uint64 last_msg_hash:bits256 = ProcessedUpto;
// key is [ shard:uint64 mc_seqno:uint32 ]
_ (HashmapE 96 ProcessedUpto) = ProcessedInfo;
ihr_pending$_ import_lt:uint64 = IhrPendingSince;
_ (HashmapE 320 IhrPendingSince) = IhrPendingInfo;
_ out_queue:OutMsgQueue proc_info:ProcessedInfo
ihr_pending:IhrPendingInfo = OutMsgQueueInfo;
//
storage_used$_ cells:(VarUInteger 7) bits:(VarUInteger 7)
public_cells:(VarUInteger 7) = StorageUsed;
storage_used_short$_ cells:(VarUInteger 7)
bits:(VarUInteger 7) = StorageUsedShort;
storage_info$_ used:StorageUsed last_paid:uint32
due_payment:(Maybe Grams) = StorageInfo;
account_none$0 = Account;
account$1 addr:MsgAddressInt storage_stat:StorageInfo
storage:AccountStorage = Account;
account_storage$_ last_trans_lt:uint64
balance:CurrencyCollection state:AccountState
= AccountStorage;
account_uninit$00 = AccountState;
account_active$1 _:StateInit = AccountState;
account_frozen$01 state_hash:bits256 = AccountState;
acc_state_uninit$00 = AccountStatus;
acc_state_frozen$01 = AccountStatus;
acc_state_active$10 = AccountStatus;
acc_state_nonexist$11 = AccountStatus;
/* duplicates
tick_tock$_ tick:Bool tock:Bool = TickTock;
_ split_depth:(Maybe (## 5)) special:(Maybe TickTock)
code:(Maybe ^Cell) data:(Maybe ^Cell)
library:(Maybe ^Cell) = StateInit;
*/
account_descr$_ account:^Account last_trans_hash:bits256
last_trans_lt:uint64 = ShardAccount;
depth_balance$_ split_depth:(#<= 30) balance:CurrencyCollection = DepthBalanceInfo;
_ (HashmapAugE 256 ShardAccount DepthBalanceInfo) = ShardAccounts;
transaction$0111 account_addr:bits256 lt:uint64
prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32
outmsg_cnt:uint15
orig_status:AccountStatus end_status:AccountStatus
^[ in_msg:(Maybe ^(Message Any)) out_msgs:(HashmapE 15 ^(Message Any)) ]
total_fees:CurrencyCollection state_update:^(HASH_UPDATE Account)
description:^TransactionDescr = Transaction;
!merkle_update#02 {X:Type} old_hash:bits256 new_hash:bits256
old:^X new:^X = MERKLE_UPDATE X;
update_hashes#72 {X:Type} old_hash:bits256 new_hash:bits256
= HASH_UPDATE X;
acc_trans#5 account_addr:bits256
transactions:(HashmapAug 64 ^Transaction CurrencyCollection)
state_update:^(HASH_UPDATE Account)
= AccountBlock;
_ (HashmapAugE 256 AccountBlock CurrencyCollection) = ShardAccountBlocks;
//
tr_phase_storage$_ storage_fees_collected:Grams
storage_fees_due:(Maybe Grams)
status_change:AccStatusChange
= TrStoragePhase;
acst_unchanged$0 = AccStatusChange; // x -> x
acst_frozen$10 = AccStatusChange; // init -> frozen
acst_deleted$11 = AccStatusChange; // frozen -> deleted
tr_phase_credit$_ due_fees_collected:(Maybe Grams)
credit:CurrencyCollection = TrCreditPhase;
tr_phase_compute_skipped$0 reason:ComputeSkipReason
= TrComputePhase;
tr_phase_compute_vm$1 success:Bool msg_state_used:Bool
account_activated:Bool gas_fees:Grams
^[ gas_used:(VarUInteger 7)
gas_limit:(VarUInteger 7) gas_credit:(Maybe (VarUInteger 3))
mode:int8 exit_code:int32 exit_arg:(Maybe int32)
vm_steps:uint32
vm_init_state_hash:bits256 vm_final_state_hash:bits256 ]
= TrComputePhase;
cskip_no_state$00 = ComputeSkipReason;
cskip_bad_state$01 = ComputeSkipReason;
cskip_no_gas$10 = ComputeSkipReason;
tr_phase_action$_ success:Bool valid:Bool no_funds:Bool
status_change:AccStatusChange
total_fwd_fees:(Maybe Grams) total_action_fees:(Maybe Grams)
result_code:int32 result_arg:(Maybe int32) tot_actions:uint16
spec_actions:uint16 skipped_actions:uint16 msgs_created:uint16
action_list_hash:bits256 tot_msg_size:StorageUsedShort
= TrActionPhase;
tr_phase_bounce_negfunds$00 = TrBouncePhase;
tr_phase_bounce_nofunds$01 msg_size:StorageUsedShort
req_fwd_fees:Grams = TrBouncePhase;
tr_phase_bounce_ok$1 msg_size:StorageUsedShort
msg_fees:Grams fwd_fees:Grams = TrBouncePhase;
//
trans_ord$0000 credit_first:Bool
storage_ph:(Maybe TrStoragePhase)
credit_ph:(Maybe TrCreditPhase)
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
aborted:Bool bounce:(Maybe TrBouncePhase)
destroyed:Bool
= TransactionDescr;
trans_storage$0001 storage_ph:TrStoragePhase
= TransactionDescr;
trans_tick_tock$001 is_tock:Bool storage:TrStoragePhase
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
aborted:Bool destroyed:Bool = TransactionDescr;
//
split_merge_info$_ cur_shard_pfx_len:(## 6)
acc_split_depth:(## 6) this_addr:bits256 sibling_addr:bits256
= SplitMergeInfo;
trans_split_prepare$0100 split_info:SplitMergeInfo
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
aborted:Bool destroyed:Bool
= TransactionDescr;
trans_split_install$0101 split_info:SplitMergeInfo
prepare_transaction:^Transaction
installed:Bool = TransactionDescr;
trans_merge_prepare$0110 split_info:SplitMergeInfo
storage_ph:TrStoragePhase aborted:Bool
= TransactionDescr;
trans_merge_install$0111 split_info:SplitMergeInfo
prepare_transaction:^Transaction
credit_ph:(Maybe TrCreditPhase)
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
aborted:Bool destroyed:Bool
= TransactionDescr;
smc_info#076ef1ea actions:uint16 msgs_sent:uint16
unixtime:uint32 block_lt:uint64 trans_lt:uint64
rand_seed:bits256 balance_remaining:CurrencyCollection
myself:MsgAddressInt = SmartContractInfo;
//
//
out_list_empty$_ = OutList 0;
out_list$_ {n:#} prev:^(OutList n) action:OutAction
= OutList (n + 1);
action_send_msg#0ec3c86d mode:(## 8)
out_msg:^(MessageRelaxed Any) = OutAction;
action_set_code#ad4de08e new_code:^Cell = OutAction;
action_reserve_currency#36e6b809 mode:(## 8)
currency:CurrencyCollection = OutAction;
out_list_node$_ prev:^Cell action:OutAction = OutListNode;
//
//
shard_ident$00 shard_pfx_bits:(#<= 60)
workchain_id:int32 shard_prefix:uint64 = ShardIdent;
ext_blk_ref$_ end_lt:uint64
seq_no:uint32 root_hash:bits256 file_hash:bits256
= ExtBlkRef;
block_id_ext$_ shard_id:ShardIdent seq_no:uint32
root_hash:bits256 file_hash:bits256 = BlockIdExt;
master_info$_ master:ExtBlkRef = BlkMasterInfo;
shard_state#9023afe2 global_id:int32
shard_id:ShardIdent
seq_no:uint32 vert_seq_no:#
gen_utime:uint32 gen_lt:uint64
min_ref_mc_seqno:uint32
out_msg_queue_info:^OutMsgQueueInfo
before_split:(## 1)
accounts:^ShardAccounts
^[ overload_history:uint64 underload_history:uint64
total_balance:CurrencyCollection
total_validator_fees:CurrencyCollection
libraries:(HashmapE 256 LibDescr)
master_ref:(Maybe BlkMasterInfo) ]
custom:(Maybe ^McStateExtra)
= ShardStateUnsplit;
_ ShardStateUnsplit = ShardState;
split_state#5f327da5 left:^ShardStateUnsplit right:^ShardStateUnsplit = ShardState;
shared_lib_descr$00 lib:^Cell publishers:(Hashmap 256 True)
= LibDescr;
block_info#9bc7a987 version:uint32
not_master:(## 1)
after_merge:(## 1) before_split:(## 1)
after_split:(## 1)
want_split:Bool want_merge:Bool
key_block:Bool vert_seqno_incr:(## 1)
flags:(## 8)
seq_no:# vert_seq_no:# { vert_seq_no >= vert_seqno_incr }
{ prev_seq_no:# } { ~prev_seq_no + 1 = seq_no }
shard:ShardIdent gen_utime:uint32
start_lt:uint64 end_lt:uint64
gen_validator_list_hash_short:uint32
gen_catchain_seqno:uint32
min_ref_mc_seqno:uint32
prev_key_block_seqno:uint32
master_ref:not_master?^BlkMasterInfo
prev_ref:^(BlkPrevInfo after_merge)
prev_vert_ref:vert_seq_no?^(BlkPrevInfo 0)
= BlockInfo;
prev_blk_info$_ prev:ExtBlkRef = BlkPrevInfo 0;
prev_blks_info$_ prev1:^ExtBlkRef prev2:^ExtBlkRef = BlkPrevInfo 1;
block#11ef55aa global_id:int32
info:^BlockInfo value_flow:^ValueFlow
state_update:^(MERKLE_UPDATE ShardState)
extra:^BlockExtra = Block;
block_extra in_msg_descr:^InMsgDescr
out_msg_descr:^OutMsgDescr
account_blocks:^ShardAccountBlocks
rand_seed:bits256
created_by:bits256
custom:(Maybe ^McBlockExtra) = BlockExtra;
//
value_flow ^[ from_prev_blk:CurrencyCollection
to_next_blk:CurrencyCollection
imported:CurrencyCollection
exported:CurrencyCollection ]
fees_collected:CurrencyCollection
^[
fees_imported:CurrencyCollection
recovered:CurrencyCollection
created:CurrencyCollection
minted:CurrencyCollection
] = ValueFlow;
//
//
bt_leaf$0 {X:Type} leaf:X = BinTree X;
bt_fork$1 {X:Type} left:^(BinTree X) right:^(BinTree X)
= BinTree X;
fsm_none$0 = FutureSplitMerge;
fsm_split$10 split_utime:uint32 interval:uint32 = FutureSplitMerge;
fsm_merge$11 merge_utime:uint32 interval:uint32 = FutureSplitMerge;
shard_descr#b seq_no:uint32 reg_mc_seqno:uint32
start_lt:uint64 end_lt:uint64
root_hash:bits256 file_hash:bits256
before_split:Bool before_merge:Bool
want_split:Bool want_merge:Bool
nx_cc_updated:Bool flags:(## 3) { flags = 0 }
next_catchain_seqno:uint32 next_validator_shard:uint64
min_ref_mc_seqno:uint32 gen_utime:uint32
split_merge_at:FutureSplitMerge
fees_collected:CurrencyCollection
funds_created:CurrencyCollection = ShardDescr;
_ (HashmapE 32 ^(BinTree ShardDescr)) = ShardHashes;
bta_leaf$0 {X:Type} {Y:Type} extra:Y leaf:X = BinTreeAug X Y;
bta_fork$1 {X:Type} {Y:Type} left:^(BinTreeAug X Y)
right:^(BinTreeAug X Y) extra:Y = BinTreeAug X Y;
_ fees:CurrencyCollection create:CurrencyCollection = ShardFeeCreated;
_ (HashmapAugE 96 ShardFeeCreated ShardFeeCreated) = ShardFees;
_ config_addr:bits256 config:^(Hashmap 32 ^Cell)
= ConfigParams;
validator_info$_
validator_list_hash_short:uint32
catchain_seqno:uint32
nx_cc_updated:Bool
= ValidatorInfo;
validator_base_info$_
validator_list_hash_short:uint32
catchain_seqno:uint32
= ValidatorBaseInfo;
_ key:Bool max_end_lt:uint64 = KeyMaxLt;
_ key:Bool blk_ref:ExtBlkRef = KeyExtBlkRef;
_ (HashmapAugE 32 KeyExtBlkRef KeyMaxLt) = OldMcBlocksInfo;
masterchain_state_extra#cc26
shard_hashes:ShardHashes
config:ConfigParams
^[ flags:(## 16) { flags = 0 }
validator_info:ValidatorInfo
prev_blocks:OldMcBlocksInfo
after_key_block:Bool
last_key_block:(Maybe ExtBlkRef) ]
global_balance:CurrencyCollection
= McStateExtra;
ed25519_pubkey#8e81278a pubkey:bits256 = SigPubKey; // 288 bits
ed25519_signature#5 R:bits256 s:bits256 = CryptoSignatureSimple; // 516 bits
_ CryptoSignatureSimple = CryptoSignature;
sig_pair$_ node_id_short:bits256 sign:CryptoSignature = CryptoSignaturePair; // 256+x ~ 772 bits
certificate#4 temp_key:SigPubKey valid_since:uint32 valid_until:uint32 = Certificate; // 356 bits
certificate_env#a419b7d certificate:Certificate = CertificateEnv; // 384 bits
signed_certificate$_ certificate:Certificate certificate_signature:CryptoSignature
= SignedCertificate; // 356+516 = 872 bits
// certificate_signature is the signature of CertificateEnv (with embedded certificate) with persistent key
chained_signature#f signed_cert:^SignedCertificate temp_key_signature:CryptoSignatureSimple
= CryptoSignature; // 4+(356+516)+516 = 520 bits+ref (1392 bits total)
// temp_key_signature is the signature of whatever was originally intended to be signed with temp_key from certificate
masterchain_block_extra#cca5
key_block:(## 1)
shard_hashes:ShardHashes
shard_fees:ShardFees
^[ prev_blk_signatures:(HashmapE 16 CryptoSignaturePair)
recover_create_msg:(Maybe ^InMsg)
mint_msg:(Maybe ^InMsg) ]
config:key_block?ConfigParams
= McBlockExtra;
//
// CONFIGURATION PARAMETERS
//
validator#53 public_key:SigPubKey weight:uint64 = ValidatorDescr;
validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = ValidatorDescr;
validators#11 utime_since:uint32 utime_until:uint32
total:(## 16) main:(## 16) { main <= total } { main >= 1 }
list:(Hashmap 16 ValidatorDescr) = ValidatorSet;
_ config_addr:bits256 = ConfigParam 0;
_ elector_addr:bits256 = ConfigParam 1;
_ minter_addr:bits256 = ConfigParam 2; // ConfigParam 0 is used if absent
_ fee_collector_addr:bits256 = ConfigParam 3; // ConfigParam 1 is used if absent
_ dns_root_addr:bits256 = ConfigParam 4; // root TON DNS resolver
_ mint_new_price:Grams mint_add_price:Grams = ConfigParam 6;
_ to_mint:ExtraCurrencyCollection = ConfigParam 7;
capabilities#c4 version:uint32 capabilities:uint64 = GlobalVersion;
_ GlobalVersion = ConfigParam 8; // all zero if absent
_ mandatory_params:(Hashmap 32 True) = ConfigParam 9;
wfmt_basic#1 vm_version:int32 vm_mode:uint64 = WorkchainFormat 1;
wfmt_ext#0 min_addr_len:(## 12) max_addr_len:(## 12) addr_len_step:(## 12)
{ min_addr_len >= 64 } { min_addr_len <= max_addr_len }
{ max_addr_len <= 1023 } { addr_len_step <= 1023 }
workchain_type_id:(## 32) { workchain_type_id >= 1 }
= WorkchainFormat 0;
workchain#a6 enabled_since:uint32 actual_min_split:(## 8)
min_split:(## 8) max_split:(## 8) { actual_min_split <= min_split }
//workchain#a5 enabled_since:uint32 min_split:(## 8) max_split:(## 8)
// { min_split <= max_split } { max_split <= 60 }
basic:(## 1) active:Bool accept_msgs:Bool flags:(## 13) { flags = 0 }
zerostate_root_hash:bits256 zerostate_file_hash:bits256
version:uint32 format:(WorkchainFormat basic)
= WorkchainDescr;
_ workchains:(HashmapE 32 WorkchainDescr) = ConfigParam 12;
block_grams_created#6b masterchain_block_fee:Grams basechain_block_fee:Grams
= BlockCreateFees;
_ BlockCreateFees = ConfigParam 14;
_ validators_elected_for:uint32 elections_start_before:uint32
elections_end_before:uint32 stake_held_for:uint32
= ConfigParam 15;
_ max_validators:(## 16) max_main_validators:(## 16) min_validators:(## 16)
{ max_validators >= max_main_validators }
{ max_main_validators >= min_validators }
{ min_validators >= 1 }
= ConfigParam 16;
_ min_stake:Grams max_stake:Grams min_total_stake:Grams max_stake_factor:uint32 = ConfigParam 17;
_#cc utime_since:uint32 bit_price_ps:uint64 cell_price_ps:uint64
mc_bit_price_ps:uint64 mc_cell_price_ps:uint64 = StoragePrices;
_ (Hashmap 32 StoragePrices) = ConfigParam 18;
gas_prices#dd gas_price:uint64 gas_limit:uint64 gas_credit:uint64
block_gas_limit:uint64 freeze_due_limit:uint64 delete_due_limit:uint64
= GasLimitsPrices;
config_mc_gas_prices#_ GasLimitsPrices = ConfigParam 20;
config_gas_prices#_ GasLimitsPrices = ConfigParam 21;
param_limits#c3 underload:# soft_limit:# { underload <= soft_limit }
hard_limit:# { soft_limit <= hard_limit } = ParamLimits;
block_limits#5d bytes:ParamLimits gas:ParamLimits lt_delta:ParamLimits
= BlockLimits;
config_mc_block_limits#_ BlockLimits = ConfigParam 22;
config_block_limits#_ BlockLimits = ConfigParam 23;
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms
// ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor)/2^16) nanograms
// bits in the root cell of a message are not included in msg.bits (lump_price pays for them)
msg_forward_prices#ea lump_price:uint64 bit_price:uint64 cell_price:uint64
ihr_price_factor:uint32 first_frac:uint16 next_frac:uint16 = MsgForwardPrices;
// used for messages to/from masterchain
config_mc_fwd_prices#_ MsgForwardPrices = ConfigParam 24;
// used for all other messages
config_fwd_prices#_ MsgForwardPrices = ConfigParam 25;
catchain_config#c1 mc_catchain_lifetime:uint32 shard_catchain_lifetime:uint32
shard_validators_lifetime:uint32 shard_validators_num:uint32 = CatchainConfig;
consensus_config#d6 round_candidates:# { round_candidates >= 1 }
next_candidate_delay_ms:uint32 consensus_timeout_ms:uint32
fast_attempts:uint32 attempt_duration:uint32 catchain_max_deps:uint32
max_block_bytes:uint32 max_collated_bytes:uint32 = ConsensusConfig;
_ CatchainConfig = ConfigParam 28;
_ ConsensusConfig = ConfigParam 29;
_ fundamental_smc_addr:(HashmapE 256 True) = ConfigParam 31;
_ prev_validators:ValidatorSet = ConfigParam 32;
_ prev_temp_validators:ValidatorSet = ConfigParam 33;
_ cur_validators:ValidatorSet = ConfigParam 34;
_ cur_temp_validators:ValidatorSet = ConfigParam 35;
_ next_validators:ValidatorSet = ConfigParam 36;
_ next_temp_validators:ValidatorSet = ConfigParam 37;
validator_temp_key#3 adnl_addr:bits256 temp_public_key:SigPubKey seqno:# valid_until:uint32 = ValidatorTempKey;
signed_temp_key#4 key:^ValidatorTempKey signature:CryptoSignature = ValidatorSignedTempKey;
_ (HashmapE 256 ValidatorSignedTempKey) = ConfigParam 39;
//
// PROOFS
//
block_signatures_pure#_ sig_count:uint32 sig_weight:uint64
signatures:(HashmapE 16 CryptoSignaturePair) = BlockSignaturesPure;
block_signatures#11 validator_info:ValidatorBaseInfo pure_signatures:BlockSignaturesPure = BlockSignatures;
block_proof#c3 proof_for:BlockIdExt root:^Cell signatures:(Maybe ^BlockSignatures) = BlockProof;
chain_empty$_ = ProofChain 0;
chain_link$_ {n:#} root:^Cell prev:n?^(ProofChain n) = ProofChain (n + 1);
top_block_descr#d5 proof_for:BlockIdExt signatures:(Maybe ^BlockSignatures)
len:(## 8) { len >= 1 } { len <= 8 } chain:(ProofChain len) = TopBlockDescr;
//
// COLLATED DATA
//
top_block_descr_set#4ac789f3 collection:(HashmapE 96 ^TopBlockDescr) = TopBlockDescrSet;

View file

@ -0,0 +1,294 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "check-proof.h"
#include "block/block.h"
#include "block/block-parse.h"
#include "block/block-auto.h"
#include "block/mc-config.h"
#include "ton/ton-shard.h"
#include "vm/cells/MerkleProof.h"
namespace block {
td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blkid, ton::Bits256* store_shard_hash_to,
bool check_state_hash) {
ton::RootHash vhash{root->get_hash().bits()};
if (vhash != blkid.root_hash) {
return td::Status::Error(PSTRING() << " block header for block " << blkid.to_str() << " has incorrect root hash "
<< vhash.to_hex() << " instead of " << blkid.root_hash.to_hex());
}
std::vector<ton::BlockIdExt> prev;
ton::BlockIdExt mc_blkid;
bool after_split;
TRY_STATUS(block::unpack_block_prev_blk_try(root, blkid, prev, mc_blkid, after_split));
block::gen::Block::Record blk;
block::gen::BlockInfo::Record info;
if (!(tlb::unpack_cell(root, blk) && tlb::unpack_cell(blk.info, info))) {
return td::Status::Error(std::string{"cannot unpack header for block "} + blkid.to_str());
}
if (store_shard_hash_to) {
vm::CellSlice upd_cs{vm::NoVmSpec(), blk.state_update};
if (!(upd_cs.is_special() && upd_cs.prefetch_long(8) == 4 // merkle update
&& upd_cs.size_ext() == 0x20228)) {
return td::Status::Error("invalid Merkle update in block header");
}
auto upd_hash = upd_cs.prefetch_ref(1)->get_hash(0);
if (!check_state_hash) {
*store_shard_hash_to = upd_hash.bits();
} else if (store_shard_hash_to->compare(upd_hash.bits())) {
return td::Status::Error(PSTRING() << "state hash mismatch in block header of " << blkid.to_str()
<< " : header declares " << upd_hash.bits().to_hex(256) << " expected "
<< store_shard_hash_to->to_hex());
}
}
return td::Status::OK();
}
td::Result<td::Bits256> check_state_proof(ton::BlockIdExt blkid, td::Slice proof) {
TRY_RESULT(proof_root, vm::std_boc_deserialize(proof));
auto virt_root = vm::MerkleProof::virtualize(std::move(proof_root), 1);
if (virt_root.is_null()) {
return td::Status::Error("account state proof is invalid");
}
td::Bits256 state_hash;
TRY_STATUS(check_block_header_proof(std::move(virt_root), blkid, &state_hash));
return state_hash;
}
td::Result<Ref<vm::Cell>> check_extract_state_proof(ton::BlockIdExt blkid, td::Slice proof, td::Slice data) {
try {
TRY_RESULT(state_hash, check_state_proof(blkid, proof));
TRY_RESULT(state_root, vm::std_boc_deserialize(data));
auto state_virt_root = vm::MerkleProof::virtualize(std::move(state_root), 1);
if (state_virt_root.is_null()) {
return td::Status::Error("account state proof is invalid");
}
if (state_hash != state_virt_root->get_hash().bits()) {
return td::Status::Error("root hash mismatch in the shardchain state proof");
}
return std::move(state_virt_root);
} catch (vm::VmError& err) {
return td::Status::Error(PSLICE() << "error scanning shard state proof: " << err.get_msg());
} catch (vm::VmVirtError& err) {
return td::Status::Error(PSLICE() << "virtualization error scanning shard state proof: " << err.get_msg());
}
}
td::Status check_shard_proof(ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td::Slice shard_proof) {
if (blk == shard_blk) {
if (!shard_proof.empty()) {
LOG(WARNING) << "Unexpected non-empty shard proof";
}
return td::Status::OK();
}
if (!blk.is_masterchain() || !blk.is_valid_full()) {
return td::Status::Error(PSLICE() << "reference block " << blk.to_str()
<< " for a getAccountState query must belong to the masterchain");
}
TRY_RESULT_PREFIX(P_roots, vm::std_boc_deserialize_multi(std::move(shard_proof)),
"cannot deserialize shard configuration proof");
if (P_roots.size() != 2) {
return td::Status::Error("shard configuration proof must have exactly two roots");
}
try {
auto mc_state_root = vm::MerkleProof::virtualize(std::move(P_roots[1]), 1);
if (mc_state_root.is_null()) {
return td::Status::Error("shard configuration proof is invalid");
}
ton::Bits256 mc_state_hash = mc_state_root->get_hash().bits();
TRY_STATUS_PREFIX(
check_block_header_proof(vm::MerkleProof::virtualize(std::move(P_roots[0]), 1), blk, &mc_state_hash, true),
"error in shard configuration block header proof :");
block::gen::ShardStateUnsplit::Record sstate;
if (!(tlb::unpack_cell(mc_state_root, sstate))) {
return td::Status::Error("cannot unpack masterchain state header");
}
auto shards_dict = block::ShardConfig::extract_shard_hashes_dict(std::move(mc_state_root));
if (!shards_dict) {
return td::Status::Error("cannot extract shard configuration dictionary from proof");
}
vm::CellSlice cs;
ton::ShardIdFull true_shard;
if (!block::ShardConfig::get_shard_hash_raw_from(*shards_dict, cs, shard_blk.shard_full(), true_shard)) {
return td::Status::Error(PSLICE() << "masterchain state contains no information for shard "
<< shard_blk.shard_full().to_str());
}
auto shard_info = block::McShardHash::unpack(cs, true_shard);
if (shard_info.is_null()) {
return td::Status::Error(PSLICE() << "cannot unpack information for shard " << shard_blk.shard_full().to_str()
<< " from masterchain state");
}
if (shard_info->top_block_id() != shard_blk) {
return td::Status::Error(PSLICE() << "shard configuration mismatch: expected to find block " << shard_blk.to_str()
<< " , found " << shard_info->top_block_id().to_str());
}
} catch (vm::VmError err) {
return td::Status::Error(PSLICE() << "error while traversing shard configuration proof : " << err.get_msg());
} catch (vm::VmVirtError err) {
return td::Status::Error(PSLICE() << "virtualization error while traversing shard configuration proof : "
<< err.get_msg());
}
return td::Status::OK();
}
td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const block::StdAddress& addr,
td::Ref<vm::Cell> root, ton::LogicalTime* last_trans_lt, ton::Bits256* last_trans_hash) {
TRY_RESULT_PREFIX(Q_roots, vm::std_boc_deserialize_multi(std::move(proof)), "cannot deserialize account proof");
if (Q_roots.size() != 2) {
return td::Status::Error(PSLICE() << "account state proof must have exactly two roots");
}
if (last_trans_lt) {
last_trans_hash->set_zero();
}
try {
auto state_root = vm::MerkleProof::virtualize(std::move(Q_roots[1]), 1);
if (state_root.is_null()) {
return td::Status::Error("account state proof is invalid");
}
ton::Bits256 state_hash = state_root->get_hash().bits();
TRY_STATUS_PREFIX(
check_block_header_proof(vm::MerkleProof::virtualize(std::move(Q_roots[0]), 1), shard_blk, &state_hash, true),
"error in account shard block header proof : ");
block::gen::ShardStateUnsplit::Record sstate;
if (!(tlb::unpack_cell(std::move(state_root), sstate))) {
return td::Status::Error("cannot unpack state header");
}
vm::AugmentedDictionary accounts_dict{vm::load_cell_slice(sstate.accounts).prefetch_ref(), 256,
block::tlb::aug_ShardAccounts};
auto acc_csr = accounts_dict.lookup(addr.addr);
if (acc_csr.not_null()) {
if (root.is_null()) {
return td::Status::Error(PSLICE() << "account state proof shows that account state for " << addr
<< " must be non-empty, but it actually is empty");
}
block::gen::ShardAccount::Record acc_info;
if (!tlb::csr_unpack(std::move(acc_csr), acc_info)) {
return td::Status::Error("cannot unpack ShardAccount from proof");
}
if (acc_info.account->get_hash().bits().compare(root->get_hash().bits(), 256)) {
return td::Status::Error(PSLICE() << "account state hash mismatch: Merkle proof expects "
<< acc_info.account->get_hash().bits().to_hex(256)
<< " but received data has " << root->get_hash().bits().to_hex(256));
}
if (last_trans_hash) {
*last_trans_hash = acc_info.last_trans_hash;
}
if (last_trans_lt) {
*last_trans_lt = acc_info.last_trans_lt;
}
} else if (root.not_null()) {
return td::Status::Error(PSLICE() << "account state proof shows that account state for " << addr
<< " must be empty, but it is not");
}
} catch (vm::VmError err) {
return td::Status::Error(PSLICE() << "error while traversing account proof : " << err.get_msg());
} catch (vm::VmVirtError err) {
return td::Status::Error(PSLICE() << "virtualization error while traversing account proof : " << err.get_msg());
}
return td::Status::OK();
}
td::Result<AccountState::Info> AccountState::validate(ton::BlockIdExt ref_blk, block::StdAddress addr) const {
TRY_RESULT_PREFIX(root, vm::std_boc_deserialize(state.as_slice(), true), "cannot deserialize account state");
LOG(INFO) << "got account state for " << addr << " with respect to blocks " << blk.to_str()
<< (shard_blk == blk ? "" : std::string{" and "} + shard_blk.to_str());
if (blk != ref_blk && ref_blk.id.seqno != ~0U) {
return td::Status::Error(PSLICE() << "obtained getAccountState() for a different reference block " << blk.to_str()
<< " instead of requested " << ref_blk.to_str());
}
if (!shard_blk.is_valid_full()) {
return td::Status::Error(PSLICE() << "shard block id " << shard_blk.to_str() << " in answer is invalid");
}
if (!ton::shard_contains(shard_blk.shard_full(), ton::extract_addr_prefix(addr.workchain, addr.addr))) {
return td::Status::Error(PSLICE() << "received data from shard block " << shard_blk.to_str()
<< " that cannot contain requested account");
}
TRY_STATUS(block::check_shard_proof(blk, shard_blk, shard_proof.as_slice()));
Info res;
TRY_STATUS(
block::check_account_proof(proof.as_slice(), shard_blk, addr, root, &res.last_trans_lt, &res.last_trans_hash));
res.root = std::move(root);
return res;
}
td::Result<Transaction::Info> Transaction::validate() {
if (root.is_null()) {
return td::Status::Error("transactions are expected to be non-empty");
}
if (hash != root->get_hash().bits()) {
return td::Status::Error(PSLICE() << "transaction hash mismatch: expected " << hash.to_hex() << ", found "
<< root->get_hash().bits().to_hex(256));
}
block::gen::Transaction::Record trans;
if (!tlb::unpack_cell(root, trans)) {
return td::Status::Error("cannot unpack transaction #");
}
if (trans.lt != lt) {
return td::Status::Error(PSLICE() << "transaction lt mismatch: expected " << lt << ", found " << trans.lt);
}
Info res;
res.blkid = blkid;
res.prev_trans_lt = trans.prev_trans_lt;
res.prev_trans_hash = trans.prev_trans_hash;
res.transaction = root;
return std::move(res);
}
td::Result<TransactionList::Info> TransactionList::validate() const {
if (blkids.empty()) {
return td::Status::Error("transaction list must be non-empty");
}
auto R = vm::std_boc_deserialize_multi(std::move(transactions_boc));
if (R.is_error()) {
return td::Status::Error("cannot deserialize transactions BoC");
}
auto list = R.move_as_ok();
if (list.size() != blkids.size()) {
return td::Status::Error(PSLICE() << "transaction list size " << list.size()
<< " must be equal to the size of block id list " << blkids.size());
}
size_t c = 0;
Info res;
auto current_lt = lt;
auto current_hash = hash;
for (auto& root : list) {
const auto& blkid = blkids[c++];
Transaction transaction;
transaction.blkid = std::move(blkid);
transaction.lt = current_lt;
transaction.hash = current_hash;
transaction.root = root;
TRY_RESULT(info, transaction.validate());
current_lt = info.prev_trans_lt;
current_hash = info.prev_trans_hash;
res.transactions.push_back(std::move(info));
}
return std::move(res);
}
} // namespace block

View file

@ -0,0 +1,80 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "vm/cells.h"
#include "block/block.h"
namespace block {
using td::Ref;
td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blkid,
ton::Bits256* store_shard_hash_to = nullptr, bool check_state_hash = false);
td::Status check_shard_proof(ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td::Slice shard_proof);
td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const block::StdAddress& addr,
td::Ref<vm::Cell> root, ton::LogicalTime* last_trans_lt = nullptr,
ton::Bits256* last_trans_hash = nullptr);
td::Result<td::Bits256> check_state_proof(ton::BlockIdExt blkid, td::Slice proof);
td::Result<Ref<vm::Cell>> check_extract_state_proof(ton::BlockIdExt blkid, td::Slice proof, td::Slice data);
struct AccountState {
ton::BlockIdExt blk;
ton::BlockIdExt shard_blk;
td::BufferSlice shard_proof;
td::BufferSlice proof;
td::BufferSlice state;
struct Info {
td::Ref<vm::Cell> root;
ton::LogicalTime last_trans_lt = 0;
ton::Bits256 last_trans_hash;
};
td::Result<Info> validate(ton::BlockIdExt ref_blk, block::StdAddress addr) const;
};
struct Transaction {
ton::BlockIdExt blkid;
ton::LogicalTime lt;
ton::Bits256 hash;
td::Ref<vm::Cell> root;
struct Info {
ton::BlockIdExt blkid;
ton::LogicalTime prev_trans_lt;
ton::Bits256 prev_trans_hash;
td::Ref<vm::Cell> transaction;
};
td::Result<Info> validate();
};
struct TransactionList {
ton::LogicalTime lt;
ton::Bits256 hash;
std::vector<ton::BlockIdExt> blkids;
td::BufferSlice transactions_boc;
struct Info {
std::vector<Transaction::Info> transactions;
};
td::Result<Info> validate() const;
};
} // namespace block

View file

@ -0,0 +1,780 @@
/*
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-2019 Telegram Systems LLP
*/
#include <cassert>
#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include <fstream>
#include <memory>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <functional>
#include <limits>
#include <getopt.h>
#include "vm/stack.hpp"
#include "vm/boc.h"
#include "fift/Fift.h"
#include "fift/Dictionary.h"
#include "fift/SourceLookup.h"
#include "fift/words.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Parser.h"
#include "td/utils/port/path.h"
#include "td/utils/port/signals.h"
#include "block.h"
#include "block-parse.h"
#include "block-auto.h"
#include "mc-config.h"
#define PDO(__op) \
if (!(__op)) { \
ok = false; \
}
#define THRERR(__msg) \
if (!ok) { \
throw fift::IntError{__msg}; \
}
#define RETERR \
if (!ok) { \
return false; \
}
using td::Ref;
int verbosity;
enum { wc_master = -1, wc_base = 0 };
constexpr int wc_undef = std::numeric_limits<int>::min();
int workchain_id = wc_undef;
int global_id = 0;
typedef td::BitArray<256> hash_t;
struct SmcDescr {
hash_t addr;
int split_depth;
bool preinit_only;
td::RefInt256 gram_balance;
Ref<vm::DataCell> state_init; // StateInit
Ref<vm::DataCell> account; // Account
SmcDescr(const hash_t& _addr) : addr(_addr), split_depth(0), preinit_only(false) {
}
};
std::map<hash_t, SmcDescr> smart_contracts;
td::RefInt256 total_smc_balance{true, 0}, max_total_smc_balance;
struct PublicLibDescr {
Ref<vm::Cell> root;
std::set<hash_t> publishers;
PublicLibDescr(Ref<vm::Cell> _root) : root(std::move(_root)) {
}
};
std::map<hash_t, PublicLibDescr> public_libraries;
hash_t config_addr;
Ref<vm::Cell> config_param_root;
bool config_addr_set;
vm::Dictionary config_dict{32};
ton::UnixTime now;
bool set_config_smc(const SmcDescr& smc) {
if (config_addr_set || smc.preinit_only || workchain_id != wc_master || smc.split_depth) {
return false;
}
vm::CellSlice cs = load_cell_slice(smc.state_init);
bool ok = true;
PDO(block::gen::t_Maybe_natwidth_5.skip(cs) && block::gen::t_Maybe_TickTock.skip(cs) &&
block::gen::t_Maybe_Ref_Cell.skip(cs));
RETERR;
Ref<vm::Cell> data;
PDO(cs.fetch_ulong(1) == 1 && cs.fetch_ref_to(data));
THRERR("config smart contract must have non-empty data");
vm::CellSlice cs2 = load_cell_slice(data);
PDO(cs2.fetch_ref_to(data));
THRERR("first reference in config smart contract data must point to initial configuration");
PDO(block::valid_config_data(data, smc.addr));
THRERR("invalid smart contract configuration data");
config_addr = smc.addr;
config_param_root = std::move(data);
config_addr_set = true;
if (verbosity > 2) {
std::cerr << "set smart contract " << config_addr << " as the configuration smart contract with configuration:\n";
load_cell_slice(config_param_root).print_rec(std::cerr);
}
return true;
}
void interpret_set_workchain(vm::Stack& stack) {
workchain_id = stack.pop_smallint_range(0x7fffffff, -0x7fffffff);
}
void interpret_get_workchain(vm::Stack& stack) {
stack.push_smallint(workchain_id);
}
void interpret_set_global_id(vm::Stack& stack) {
global_id = stack.pop_smallint_range(0x7fffffff, -0x7fffffff);
}
void interpret_get_global_id(vm::Stack& stack) {
stack.push_smallint(global_id);
}
void interpret_get_verbosity(vm::Stack& stack) {
stack.push_smallint(GET_VERBOSITY_LEVEL());
}
void interpret_set_verbosity(vm::Stack& stack) {
int x = stack.pop_smallint_range(15);
SET_VERBOSITY_LEVEL(x);
}
void interpret_set_config_smartcontract(vm::Stack& stack) {
if (workchain_id != wc_master) {
throw fift::IntError{"configuration smart contract may be selected in masterchain only"};
}
if (config_addr_set) {
throw fift::IntError{"configuration smart contract already selected"};
}
td::RefInt256 int_addr = stack.pop_int_finite();
hash_t addr;
if (!int_addr->export_bits(addr.bits(), 256, false)) {
throw fift::IntError{"not a valid smart-contract address"};
}
auto it = smart_contracts.find(addr);
if (it == smart_contracts.end()) {
throw fift::IntError{"unknown smart contract"};
}
const SmcDescr& smc = it->second;
assert(smc.addr == addr);
if (smc.preinit_only) {
throw fift::IntError{"configuration smart contract must be completely initialized"};
}
if (!set_config_smc(smc)) {
throw fift::IntError{"invalid configuration smart contract"};
}
}
bool is_empty_cell(Ref<vm::Cell> cell) {
bool is_special;
auto cs = load_cell_slice_special(std::move(cell), is_special);
return !is_special && cs.empty_ext();
}
bool add_public_library(hash_t lib_addr, hash_t smc_addr, Ref<vm::Cell> lib_root) {
if (lib_root.is_null() || lib_root->get_hash().as_array() != lib_addr.as_array()) {
return false;
}
auto ins = public_libraries.emplace(lib_addr, lib_root);
PublicLibDescr& lib = ins.first->second;
lib.publishers.insert(smc_addr);
if (verbosity > 2) {
std::cerr << "added " << (ins.second ? "new " : "") << "public library " << lib_addr << " with publisher "
<< smc_addr << std::endl;
}
return true;
}
td::RefInt256 create_smartcontract(td::RefInt256 smc_addr, Ref<vm::Cell> code, Ref<vm::Cell> data,
Ref<vm::Cell> library, td::RefInt256 balance, int special, int split_depth,
int mode) {
if (is_empty_cell(code)) {
code.clear();
}
if (is_empty_cell(data)) {
data.clear();
}
if (is_empty_cell(library)) {
library.clear();
}
bool ok = true;
if (library.not_null()) {
PDO(block::valid_library_collection(library, false));
THRERR("not a valid library collection");
}
vm::CellBuilder cb;
if (!split_depth) {
PDO(cb.store_long_bool(0, 1));
} else {
PDO(cb.store_long_bool(1, 1) && cb.store_ulong_rchk_bool(split_depth, 5));
}
THRERR("invalid split_depth for a smart contract");
if (!special) {
PDO(cb.store_long_bool(0, 1));
} else {
PDO(cb.store_long_bool(1, 1) && cb.store_ulong_rchk_bool(special, 2));
}
THRERR("invalid special TickTock argument for a smart contract");
PDO(cb.store_maybe_ref(std::move(code)) && cb.store_maybe_ref(std::move(data)) && cb.store_maybe_ref(library));
THRERR("cannot store smart-contract code, data or library");
Ref<vm::DataCell> state_init = cb.finalize();
hash_t addr;
if (smc_addr.is_null()) {
addr = state_init->get_hash().as_array();
smc_addr = td::RefInt256{true};
PDO(smc_addr.write().import_bits(addr.data(), 0, 256, false));
} else if (mode == 1) {
throw fift::IntError{"cannot create uninitialized smart contracts with specified addresses"};
} else {
PDO(smc_addr->export_bits(addr.data(), 0, 256, false));
}
THRERR("cannot initialize smart-contract address");
if (verbosity > 2) {
std::cerr << "smart-contract address is ";
std::cerr << addr << " = " << smc_addr << std::endl;
}
PDO(mode || !sgn(balance));
THRERR("cannot set non-zero balance to smart contract unless it is initialized");
PDO(sgn(balance) >= 0);
THRERR("balance cannot be negative");
if (!mode) {
return smc_addr; // compute address only
}
auto it = smart_contracts.find(addr);
if (it != smart_contracts.end()) {
std::cerr << "smart contract " << addr << " already defined\n";
throw fift::IntError{"smart contract already exists"};
}
auto ins = smart_contracts.emplace(addr, addr);
assert(ins.second);
SmcDescr& smc = ins.first->second;
smc.split_depth = split_depth;
smc.preinit_only = (mode == 1);
smc.gram_balance = balance;
total_smc_balance += balance;
if (mode > 1) {
smc.state_init = std::move(state_init);
}
if (max_total_smc_balance.not_null() && total_smc_balance > max_total_smc_balance) {
throw fift::IntError{"total smart-contract balance exceeds limit"};
}
cb.reset();
PDO(cb.store_long_bool(0, 64) // account_storage$_ last_trans_lt:uint64
&& block::tlb::t_Grams.store_integer_value(cb, *balance) // balance.grams:Grams
&& cb.store_long_bool(0, 1)); // balance.other:ExtraCurrencyCollection
if (mode == 1) {
PDO(block::gen::t_AccountState.pack_account_uninit(cb));
} else {
PDO(block::gen::t_AccountState.pack_account_active(cb, vm::load_cell_slice_ref(smc.state_init)));
}
THRERR("cannot create smart-contract AccountStorage");
Ref<vm::DataCell> storage = cb.finalize();
vm::CellStorageStat stats;
PDO(stats.compute_used_storage(Ref<vm::Cell>(storage)));
if (verbosity > 2) {
std::cerr << "storage is:\n";
vm::load_cell_slice(storage).print_rec(std::cerr);
std::cerr << "stats: bits=" << stats.bits << ", cells=" << stats.cells << std::endl;
std::cerr << "block::gen::AccountStorage.validate_ref() = " << block::gen::t_AccountStorage.validate_ref(storage)
<< std::endl;
std::cerr << "block::tlb::AccountStorage.validate_ref() = " << block::tlb::t_AccountStorage.validate_ref(storage)
<< std::endl;
}
PDO(block::gen::t_AccountStorage.validate_ref(storage));
THRERR("AccountStorage of created smart-contract is invalid (?)");
cb.reset(); // build Account
PDO(cb.store_long_bool(1, 1)); // account$1
int ctor = 3; // addr_var$11
if (workchain_id >= -128 && workchain_id <= 127) {
ctor = 2; // addr_std$10
}
PDO(cb.store_long_bool(ctor, 2)); // addr_std$10 or addr_var$11
if (split_depth) {
PDO(cb.store_long_bool(1, 1) // just$1
&& cb.store_ulong_rchk_bool(split_depth, 5) // depth:(## 5)
&& cb.store_bits_bool(addr.cbits(), split_depth)); // rewrite pfx:(depth * Bit)
} else {
PDO(cb.store_long_bool(0, 1)); // nothing$0
}
PDO(cb.store_long_rchk_bool(workchain_id, ctor == 2 ? 8 : 32) && cb.store_bits_bool(addr.cbits(), 256));
THRERR("Cannot serialize addr:MsgAddressInt of the new smart contract");
// storage_stat:StorageInfo -> storage_stat.used:StorageUsed
PDO(block::store_UInt7(cb, stats.cells) // cells:(VarUInteger 7)
&& block::store_UInt7(cb, stats.bits) // bits:(VarUInteger 7)
&& block::store_UInt7(cb, stats.public_cells)); // public_cells:(VarUInteger 7)
THRERR("Cannot serialize used:StorageUsed of the new smart contract");
PDO(cb.store_long_bool(0, 33)); // last_paid:uint32 due_payment:(Maybe Grams)
PDO(cb.append_data_cell_bool(storage)); // storage:AccountStorage
THRERR("Cannot create Account of the new smart contract");
smc.account = cb.finalize();
if (verbosity > 2) {
std::cerr << "account is:\n";
vm::load_cell_slice(smc.account).print_rec(std::cerr);
std::cerr << "block::gen::Account.validate_ref() = " << block::gen::t_Account.validate_ref(smc.account)
<< std::endl;
std::cerr << "block::tlb::Account.validate_ref() = " << block::tlb::t_Account.validate_ref(smc.account)
<< std::endl;
}
PDO(block::gen::t_Account.validate_ref(smc.account));
THRERR("Account of created smart contract is invalid (?)");
if (library.not_null()) {
vm::Dictionary dict{std::move(library), 256};
ok &= dict.check_for_each([addr](Ref<vm::CellSlice> cs, td::ConstBitPtr key, int n) -> bool {
return !cs->prefetch_ulong(1) || add_public_library(key, addr, cs->prefetch_ref());
});
THRERR("Error processing libraries published by new smart contract");
}
return smc_addr;
}
// stores accounts:ShardAccounts
bool store_accounts(vm::CellBuilder& cb) {
vm::AugmentedDictionary dict{256, block::tlb::aug_ShardAccounts};
for (const auto& smc_pair : smart_contracts) {
const SmcDescr& smc = smc_pair.second;
CHECK(smc_pair.first == smc.addr);
vm::CellBuilder cb;
bool ok = cb.store_ref_bool(smc.account) // account_descr$_ acc:^Account
&& cb.store_zeroes_bool(256 + 64) // last_trans_hash:bits256 last_trans_lt:uint64
&& dict.set_builder(smc.addr.cbits(), 256, cb, vm::Dictionary::SetMode::Add);
CHECK(ok);
}
return std::move(dict).append_dict_to_bool(cb);
}
// stores libraries:(HashmapE 256 LibDescr)
bool store_public_libraries(vm::CellBuilder& cb) {
vm::Dictionary dict{256};
bool ok = true;
vm::CellBuilder empty_cb;
for (const auto& lib_pair : public_libraries) {
const PublicLibDescr pl = lib_pair.second;
PDO(pl.root->get_hash().as_array() == lib_pair.first.as_array());
vm::Dictionary publishers{256};
for (const auto& publisher : pl.publishers) {
PDO(publishers.set_builder(publisher.cbits(), 256, empty_cb, vm::Dictionary::SetMode::Add));
}
Ref<vm::Cell> root = std::move(publishers).extract_root_cell();
PDO(root.not_null());
THRERR("public library has an empty or invalid set of publishers");
vm::CellBuilder value_cb; // LibDescr
PDO(value_cb.store_long_bool(0, 2) && value_cb.store_ref_bool(pl.root) &&
value_cb.append_cellslice_bool(vm::load_cell_slice(std::move(root))));
THRERR("cannot create LibDescr for a public library");
PDO(dict.set_builder(lib_pair.first.cbits(), 256, value_cb, vm::Dictionary::SetMode::Add));
THRERR("cannot insert LibDescr of a public library into the public library collection");
}
PDO(std::move(dict).append_dict_to_bool(cb));
return ok;
}
// stores config:ConfigParams
bool store_config_params(vm::CellBuilder& cb) {
return config_addr_set && config_param_root.not_null() &&
cb.store_bits_bool(config_addr.cbits(), 256) // _ config_addr:bits256
&& cb.store_ref_bool(config_param_root); // config:^(Hashmap 32 ^Cell)
}
// stores hash of initial masterchain validator set computed from configuration parameter 34
bool store_validator_list_hash(vm::CellBuilder& cb) {
Ref<vm::Cell> vset_cell = config_dict.lookup_ref(td::BitArray<32>{34});
auto res = block::Config::unpack_validator_set(std::move(vset_cell));
if (res.is_error()) {
LOG(ERROR) << "cannot unpack current validator set: " << res.move_as_error().to_string();
return false;
}
auto vset = res.move_as_ok();
LOG_CHECK(vset) << "unpacked validator set is empty";
auto ccvc = block::Config::unpack_catchain_validators_config(config_dict.lookup_ref(td::BitArray<32>{28}));
ton::ShardIdFull shard{ton::masterchainId};
auto nodes = block::Config::do_compute_validator_set(ccvc, shard, *vset, now, 0);
LOG_CHECK(!nodes.empty()) << "validator node list in unpacked validator set is empty";
auto vset_hash = block::compute_validator_set_hash(0, shard, std::move(nodes));
LOG(DEBUG) << "initial validator set hash is " << vset_hash;
return cb.store_long_bool(vset_hash, 32);
}
// stores custom:(Maybe ^McStateExtra)
bool store_custom(vm::CellBuilder& cb) {
if (workchain_id != wc_master) {
return cb.store_long_bool(0, 1); // nothing
}
vm::CellBuilder cb2, cb3;
bool ok = true;
PDO(cb2.store_long_bool(0xcc26, 16) // masterchain_state_extra#cc26
&& cb2.store_long_bool(0, 1) // shard_hashes:ShardHashes = (HashmapE 32 ^(BinTree ShardDescr))
&& store_config_params(cb2) // config:ConfigParams
&& cb3.store_long_bool(0, 16) // ^[ flags:(## 16) { flags = 0 }
&& store_validator_list_hash(cb3) // validator_list_hash_short:uint32
&& cb3.store_long_bool(0, 32) // catchain_seqno:uint32
&& cb3.store_bool_bool(true) // nx_cc_updated:Bool
&& cb3.store_zeroes_bool(1 + 65) // prev_blocks:OldMcBlocksInfo
&& cb3.store_long_bool(2, 1 + 1) // after_key_block:Bool last_key_block:(Maybe ...)
&& cb2.store_ref_bool(cb3.finalize()) // ]
&& block::CurrencyCollection{total_smc_balance}.store(cb2) // global_balance:CurrencyCollection
&& cb.store_long_bool(1, 1) // just
&& cb.store_ref_bool(cb2.finalize()));
return ok;
}
Ref<vm::Cell> create_state() {
vm::CellBuilder cb, cb2;
now = static_cast<ton::UnixTime>(time(0));
bool ok = true;
PDO(workchain_id != wc_undef);
THRERR("workchain_id is unset, cannot generate state");
PDO(workchain_id != wc_master || config_addr_set);
THRERR("configuration smart contract must be selected");
PDO(cb.store_long_bool(0x9023afe2, 32) // shard_state#9023afe2
&& cb.store_long_bool(global_id, 32)); // global_id:int32
PDO(cb.store_long_bool(0, 8) && cb.store_long_bool(workchain_id, 32) &&
cb.store_long_bool(0, 64) // shard_id:ShardIdent
&& cb.store_long_bool(0, 32) // seq_no:#
&& cb.store_zeroes_bool(32) // vert_seq_no:#
&& cb.store_long_bool(now, 32) // gen_utime:uint32
&& cb.store_zeroes_bool(64) // gen_lt:uint64
&& cb.store_ones_bool(32) // min_ref_mc_seqno:uint32
&& cb2.store_zeroes_bool(1 + 64 + 2) // OutMsgQueueInfo
&& cb.store_ref_bool(cb2.finalize()) // out_msg_queue_info:^OutMsgQueueInfo
&& cb.store_long_bool(0, 1) // before_split:Bool
&& store_accounts(cb2) // accounts:^ShardAccounts
&& cb.store_ref_bool(cb2.finalize()) // ...
&& cb2.store_zeroes_bool(128) // ^[ overload_history:uint64 underload_history:uint64
&& block::CurrencyCollection{total_smc_balance}.store(cb2) // total_balance:CurrencyCollection
&& block::tlb::t_CurrencyCollection.null_value(cb2) // total_validator_fees:CurrencyCollection
&& store_public_libraries(cb2) // libraries:(Hashmap 256 LibDescr)
&& cb2.store_long_bool(0, 1) // master_ref:(Maybe BlkMasterInfo)
&& cb.store_ref_bool(cb2.finalize()) // ]
&& store_custom(cb)); // custom:(Maybe ^McStateExtra)
THRERR("cannot create blockchain state");
Ref<vm::Cell> cell = cb.finalize();
if (verbosity > 2) {
std::cerr << "shard_state is:\n";
vm::load_cell_slice(cell).print_rec(std::cerr);
std::cerr << "pretty-printed shard_state is:\n";
block::gen::t_ShardState.print_ref(std::cerr, cell);
std::cerr << "\n";
std::cerr << "block::gen::ShardState.validate_ref() = " << block::gen::t_ShardState.validate_ref(cell) << std::endl;
std::cerr << "block::tlb::ShardState.validate_ref() = " << block::tlb::t_ShardState.validate_ref(cell) << std::endl;
block::gen::ShardStateUnsplit::Record data;
bool ok1 = tlb::unpack_cell(cell, data);
std::cerr << "block::gen::ShardState.unpack_cell() = " << ok1 << std::endl;
if (ok1) {
std::cerr << "shard_id = " << data.shard_id
<< "; out_msg_queue_info = " << load_cell_slice(data.out_msg_queue_info)
<< "; total_balance = " << data.r1.total_balance << std::endl;
}
}
PDO(block::gen::t_ShardState.validate_ref(cell));
PDO(block::tlb::t_ShardState.validate_ref(cell));
THRERR("created an invalid ShardState record");
return cell;
}
// code (cell)
// data (cell)
// library (cell)
// balance (int)
// split_depth (int 0..32)
// special (int 0..3, +2 = tick, +1 = tock)
// [ address (uint256) ]
// mode (0 = compute address only, 1 = create uninit, 2 = create complete; +4 = with specified address)
// --> 256-bit address
void interpret_register_smartcontract(vm::Stack& stack) {
if (workchain_id == wc_undef) {
throw fift::IntError{"cannot register a smartcontract unless the workchain is specified first"};
}
td::RefInt256 spec_addr;
int mode = stack.pop_smallint_range(2 + 4); // allowed modes: 0 1 2 4 5 6
if (mode == 3) {
throw fift::IntError{"invalid mode"};
}
if (mode & 4) {
spec_addr = stack.pop_int_finite();
mode &= ~4;
}
int special = stack.pop_smallint_range(3);
if (special && workchain_id != wc_master) {
throw fift::IntError{"cannot create special smartcontracts outside of the masterchain"};
}
int split_depth = stack.pop_smallint_range(32);
td::RefInt256 balance = stack.pop_int_finite();
if (sgn(balance) < 0) {
throw fift::IntError{"initial balance of a smartcontract cannot be negative"};
}
if (sgn(balance) > 0 && !mode) {
throw fift::IntError{"cannot set non-zero balance if an account is not created"};
}
Ref<vm::Cell> library = stack.pop_cell();
Ref<vm::Cell> data = stack.pop_cell();
Ref<vm::Cell> code = stack.pop_cell();
td::RefInt256 addr = create_smartcontract(std::move(spec_addr), std::move(code), std::move(data), std::move(library),
std::move(balance), special, split_depth, mode);
if (addr.is_null()) {
throw fift::IntError{"internal error while creating smartcontract"};
}
stack.push(std::move(addr));
}
void interpret_create_state(vm::Stack& stack) {
if (!global_id) {
throw fift::IntError{
"(global) blockchain id must be set to a non-zero value: negative for test chains, positive for production"};
}
Ref<vm::Cell> state = create_state();
if (state.is_null()) {
throw fift::IntError{"could not create blockchain state"};
}
stack.push(std::move(state));
}
void interpret_get_config_dict(vm::Stack& stack) {
Ref<vm::Cell> value = config_dict.get_root_cell();
if (value.is_null()) {
stack.push_bool(false);
} else {
stack.push_cell(std::move(value));
stack.push_bool(true);
}
}
void interpret_get_config_param(vm::Stack& stack) {
int x = stack.pop_smallint_range(0x7fffffff, 0x80000000);
Ref<vm::Cell> value = config_dict.lookup_ref(td::BitArray<32>{x});
if (value.is_null()) {
stack.push_bool(false);
} else {
stack.push_cell(std::move(value));
stack.push_bool(true);
}
}
void interpret_set_config_param(vm::Stack& stack) {
int x = stack.pop_smallint_range(0x7fffffff, 0x80000000);
Ref<vm::Cell> value = stack.pop_cell();
if (verbosity > 2 && x >= 0) {
std::cerr << "setting configuration parameter #" << x << " to ";
// vm::load_cell_slice(value).print_rec(std::cerr);
block::gen::ConfigParam{x}.print_ref(std::cerr, value);
std::cerr << std::endl;
}
if (x >= 0 && !block::gen::ConfigParam{x}.validate_ref(value)) {
throw fift::IntError{"invalid value for indicated configuration parameter"};
}
if (!config_dict.set_ref(td::BitArray<32>{x}, std::move(value))) {
throw fift::IntError{"cannot set value of configuration parameter (value too long?)"};
}
}
void interpret_is_shard_state(vm::Stack& stack) {
Ref<vm::Cell> cell = stack.pop_cell();
if (verbosity > 4) {
std::cerr << "custom shard state is:\n";
vm::load_cell_slice(cell).print_rec(std::cerr);
std::cerr << "pretty-printed custom shard state is:\n";
block::gen::t_ShardState.print_ref(std::cerr, cell);
}
stack.push_bool(block::gen::t_ShardState.validate_ref(std::move(cell)));
}
void interpret_is_workchain_descr(vm::Stack& stack) {
Ref<vm::Cell> cell = stack.pop_cell();
if (verbosity > 4) {
std::cerr << "WorkchainDescr is:\n";
vm::load_cell_slice(cell).print_rec(std::cerr);
std::cerr << "pretty-printed WorkchainDescr is:\n";
block::gen::t_WorkchainDescr.print_ref(std::cerr, cell);
}
stack.push_bool(block::gen::t_WorkchainDescr.validate_ref(std::move(cell)));
}
void init_words_custom(fift::Dictionary& d) {
d.def_stack_word("verb@ ", interpret_get_verbosity);
d.def_stack_word("verb! ", interpret_set_verbosity);
d.def_stack_word("wcid@ ", interpret_get_workchain);
d.def_stack_word("wcid! ", interpret_set_workchain);
d.def_stack_word("globalid@ ", interpret_get_global_id);
d.def_stack_word("globalid! ", interpret_set_global_id);
d.def_stack_word("config@ ", interpret_get_config_param);
d.def_stack_word("config! ", interpret_set_config_param);
d.def_stack_word("(configdict) ", interpret_get_config_dict);
d.def_stack_word("register_smc ", interpret_register_smartcontract);
d.def_stack_word("set_config_smc ", interpret_set_config_smartcontract);
d.def_stack_word("create_state ", interpret_create_state);
d.def_stack_word("isShardState? ", interpret_is_shard_state);
d.def_stack_word("isWorkchainDescr? ", interpret_is_workchain_descr);
}
void usage(const char* progname) {
std::cerr
<< "Creates initial state for a TON blockchain, using configuration defined by Fift-language source files\n";
std::cerr
<< "usage: " << progname
<< " [-i] [-n] [-I <source-include-path>] {-L <library-fif-file>} <source-file1-fif> <source-file2-fif> ...\n";
std::cerr << "\t-n\tDo not preload preamble files `Fift.fif` and `CreateState.fif`\n"
"\t-i\tForce interactive mode even if explicit source file names are indicated\n"
"\t-I<source-search-path>\tSets colon-separated library source include path. If not indicated, "
"$FIFTPATH is used instead.\n"
"\t-L<library-fif-file>\tPre-loads a library source file\n"
"\t-v<verbosity-level>\tSet verbosity level\n";
std::exit(2);
}
void parse_include_path_set(std::string include_path_set, std::vector<std::string>& res) {
td::Parser parser(include_path_set);
while (!parser.empty()) {
auto path = parser.read_till_nofail(':');
if (!path.empty()) {
res.push_back(path.str());
}
parser.skip_nofail(':');
}
}
void preload_preamble(fift::Fift& fift, std::string filename, bool standard = true) {
auto status = fift.interpret_file(filename, "");
if (status.is_error()) {
LOG(ERROR) << "Error interpreting " << (standard ? "standard" : "application-specific") << " preamble file `"
<< filename << "`: " << status.error().message()
<< "\nCheck that correct include path is set by -I or by FIFTPATH environment variable, or disable "
"standard preamble by -n.\n";
std::exit(2);
}
}
int main(int argc, char* const argv[]) {
td::set_default_failure_signal_handler().ensure();
bool interactive = false;
bool fift_preload = true, no_env = false, script_mode = false;
std::vector<std::string> library_source_files, source_list;
std::vector<std::string> source_include_path;
std::string ton_db_path;
fift::Fift::Config config;
int i;
int new_verbosity_level = VERBOSITY_NAME(INFO);
while (!script_mode && (i = getopt(argc, argv, "hinsI:L:v:")) != -1) {
switch (i) {
case 'i':
interactive = true;
break;
case 'n':
fift_preload = false;
break;
case 'I':
LOG(ERROR) << source_include_path;
parse_include_path_set(optarg, source_include_path);
no_env = true;
break;
case 's':
script_mode = true;
break;
case 'L':
library_source_files.emplace_back(optarg);
break;
case 'v':
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
break;
case 'h':
default:
usage(argv[0]);
}
}
SET_VERBOSITY_LEVEL(new_verbosity_level);
while (optind < argc) {
source_list.emplace_back(argv[optind++]);
if (script_mode) {
break;
}
}
if (!no_env) {
const char* path = std::getenv("FIFTPATH");
if (path) {
parse_include_path_set(path ? path : "/usr/lib/fift", source_include_path);
}
}
std::string current_dir;
auto r_current_dir = td::realpath(".");
if (r_current_dir.is_ok()) {
current_dir = r_current_dir.move_as_ok();
source_include_path.push_back(current_dir);
}
config.source_lookup = fift::SourceLookup(std::make_unique<fift::OsFileLoader>());
for (auto& path : source_include_path) {
config.source_lookup.add_include_path(path);
}
fift::init_words_common(config.dictionary);
fift::init_words_vm(config.dictionary);
fift::init_words_ton(config.dictionary);
init_words_custom(config.dictionary);
if (script_mode) {
fift::import_cmdline_args(config.dictionary, source_list.empty() ? "" : source_list[0], argc - optind,
argv + optind);
}
fift::Fift fift(std::move(config));
if (fift_preload) {
preload_preamble(fift, "Fift.fif", true);
preload_preamble(fift, "CreateState.fif", false);
}
for (auto source : library_source_files) {
auto status = fift.interpret_file(source, "");
if (status.is_error()) {
std::cerr << "Error interpreting preloaded file `" << source << "`: " << status.error().to_string() << std::endl;
std::exit(2);
}
}
if (source_list.empty() && !interactive) {
std::cerr << "No Fift source files specified" << std::endl;
std::exit(2);
}
for (const auto& source : source_list) {
auto status = fift.interpret_file(source, current_dir);
if (status.is_error()) {
std::cerr << "Error interpreting file `" << source << "`: " << status.error().to_string() << std::endl;
std::exit(2);
}
}
if (interactive) {
fift.interpret_istream(std::cin, current_dir).ensure();
}
// show_total_cells();
}

1817
crypto/block/mc-config.cpp Normal file

File diff suppressed because it is too large Load diff

617
crypto/block/mc-config.h Normal file
View file

@ -0,0 +1,617 @@
/*
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/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "common/refcnt.hpp"
#include "vm/db/StaticBagOfCellsDb.h"
#include "vm/dict.h"
#include "ton/ton-types.h"
#include "ton/ton-shard.h"
#include "common/bitstring.h"
#include "block.h"
#include <vector>
#include <limits>
#include <map>
#include <set>
#include <cstring>
namespace block {
using td::Ref;
struct ValidatorDescr {
ton::Ed25519_PublicKey pubkey;
td::Bits256 adnl_addr;
td::uint64 weight;
td::uint64 cum_weight;
ValidatorDescr(const td::Bits256& _pubkey, td::uint64 _weight, td::uint64 _cum_weight)
: pubkey(_pubkey), weight(_weight), cum_weight(_cum_weight) {
adnl_addr.set_zero();
}
ValidatorDescr(const td::Bits256& _pubkey, td::uint64 _weight, td::uint64 _cum_weight, const td::Bits256& _adnl_addr)
: pubkey(_pubkey), adnl_addr(_adnl_addr), weight(_weight), cum_weight(_cum_weight) {
}
ValidatorDescr(const ton::Ed25519_PublicKey& _pubkey, td::uint64 _weight, td::uint64 _cum_weight)
: pubkey(_pubkey), weight(_weight), cum_weight(_cum_weight) {
adnl_addr.set_zero();
}
bool operator<(td::uint64 wt_pos) const & {
return cum_weight < wt_pos;
}
};
struct ValidatorSet {
unsigned utime_since;
unsigned utime_until;
int total;
int main;
td::uint64 total_weight;
std::vector<ValidatorDescr> list;
ValidatorSet() = default;
ValidatorSet(unsigned _since, unsigned _until, int _total, int _main = 0)
: utime_since(_since), utime_until(_until), total(_total), main(_main > 0 ? _main : _total), total_weight(0) {
}
const ValidatorDescr& operator[](unsigned i) const {
return list[i];
}
const ValidatorDescr& at_weight(td::uint64 weight_pos) const;
std::vector<ton::ValidatorDescr> export_validator_set() const;
};
#pragma pack(push, 1)
// this structure is hashed with SHA512 to produce pseudo-random bit stream in do_compute_validator_set()
// NB: all integers (including 256-bit seed) are actually big-endian
struct validator_set_descr {
unsigned char seed[32]; // seed for validator set computation, set to zero if none
td::uint64 shard;
td::int32 workchain;
td::uint32 cc_seqno;
validator_set_descr() = default;
validator_set_descr(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_, bool flag)
: shard(td::bswap64(shard_id.shard))
, workchain(td::bswap32(shard_id.workchain))
, cc_seqno(td::bswap32(cc_seqno_)) {
}
validator_set_descr(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_)
: validator_set_descr(shard_id, cc_seqno_, false) {
std::memset(seed, 0, 32);
}
validator_set_descr(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_, const unsigned char seed_[32])
: validator_set_descr(shard_id, cc_seqno_, false) {
std::memcpy(seed, seed_, 32);
}
validator_set_descr(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_, td::ConstBitPtr seed_)
: validator_set_descr(shard_id, cc_seqno_, false) {
td::BitPtr{seed}.copy_from(seed_, 256);
}
validator_set_descr(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_, const td::Bits256& seed_)
: validator_set_descr(shard_id, cc_seqno_, false) {
td::BitPtr{seed}.copy_from(seed_.cbits(), 256);
}
void incr_seed();
void hash_to(unsigned char hash_buffer[64]) const;
};
#pragma pack(pop)
class ValidatorSetPRNG {
validator_set_descr data;
union {
unsigned char hash[64];
td::uint64 hash_longs[8];
};
int pos{0}, limit{0};
public:
ValidatorSetPRNG() = default;
ValidatorSetPRNG(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_) : data(shard_id, cc_seqno_) {
}
ValidatorSetPRNG(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_, const unsigned char seed_[32])
: data(shard_id, cc_seqno_, seed_) {
}
ValidatorSetPRNG(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_, td::ConstBitPtr seed_)
: data(shard_id, cc_seqno_, std::move(seed_)) {
}
ValidatorSetPRNG(ton::ShardIdFull shard_id, ton::CatchainSeqno cc_seqno_, const td::Bits256& seed_)
: data(shard_id, cc_seqno_, seed_) {
}
td::uint64 next_ulong();
td::uint64 next_ranged(td::uint64 range); // integer in 0 .. range-1
ValidatorSetPRNG& operator>>(td::uint64& x) {
x = next_ulong();
return *this;
}
};
class McShardHashI : public td::CntObject {
public:
enum class FsmState { fsm_none, fsm_split, fsm_merge };
virtual ton::BlockIdExt top_block_id() const = 0;
virtual ton::LogicalTime start_lt() const = 0;
virtual ton::LogicalTime end_lt() const = 0;
virtual ton::UnixTime fsm_utime() const = 0;
virtual FsmState fsm_state() const = 0;
virtual ton::ShardIdFull shard() const = 0;
virtual bool before_split() const = 0;
virtual bool before_merge() const = 0;
};
struct McShardHash : public McShardHashI {
ton::BlockIdExt blk_;
ton::LogicalTime start_lt_, end_lt_;
ton::UnixTime gen_utime_{0};
ton::UnixTime fsm_utime_{0};
ton::UnixTime fsm_interval_{0};
ton::BlockSeqno min_ref_mc_seqno_{std::numeric_limits<ton::BlockSeqno>::max()};
ton::BlockSeqno reg_mc_seqno_{std::numeric_limits<ton::BlockSeqno>::max()};
FsmState fsm_{FsmState::fsm_none};
bool disabled_{false};
bool before_split_{false}, before_merge_{false}, want_split_{false}, want_merge_{false};
ton::CatchainSeqno next_catchain_seqno_{std::numeric_limits<ton::CatchainSeqno>::max()};
ton::ShardId next_validator_shard_{ton::shardIdAll};
CurrencyCollection fees_collected_, funds_created_;
McShardHash(const ton::BlockId& id, ton::LogicalTime start_lt, ton::LogicalTime end_lt, ton::UnixTime gen_utime,
const ton::BlockHash& root_hash, const ton::FileHash& file_hash, CurrencyCollection fees_collected = {},
CurrencyCollection funds_created = {},
ton::BlockSeqno reg_mc_seqno = std::numeric_limits<ton::BlockSeqno>::max(),
ton::BlockSeqno min_ref_mc_seqno = std::numeric_limits<ton::BlockSeqno>::max(),
ton::CatchainSeqno cc_seqno = std::numeric_limits<ton::CatchainSeqno>::max(), ton::ShardId val_shard = 0,
bool nx_cc_updated = false, bool before_split = false, bool before_merge = false, bool want_split = false,
bool want_merge = false)
: blk_(id, root_hash, file_hash)
, start_lt_(start_lt)
, end_lt_(end_lt)
, gen_utime_(gen_utime)
, min_ref_mc_seqno_(min_ref_mc_seqno)
, reg_mc_seqno_(reg_mc_seqno)
, before_split_(before_split)
, before_merge_(before_merge)
, want_split_(want_split)
, want_merge_(want_merge)
, next_catchain_seqno_(cc_seqno)
, next_validator_shard_(val_shard ? val_shard : id.shard)
, fees_collected_(fees_collected)
, funds_created_(funds_created) {
}
McShardHash(const ton::BlockIdExt& blk, ton::LogicalTime start_lt, ton::LogicalTime end_lt)
: blk_(blk), start_lt_(start_lt), end_lt_(end_lt) {
}
McShardHash(const McShardHash&) = default;
bool is_valid() const {
return blk_.is_valid();
}
ton::BlockIdExt top_block_id() const override final {
return blk_;
}
// ZeroStateIdExt zero_state() const override;
ton::LogicalTime start_lt() const override final {
return start_lt_;
}
ton::LogicalTime end_lt() const override final {
return end_lt_;
}
ton::UnixTime fsm_utime() const override final {
return fsm_utime_;
}
ton::UnixTime fsm_utime_end() const {
return fsm_utime_ + fsm_interval_;
}
ton::UnixTime created_at() const {
return gen_utime_;
}
FsmState fsm_state() const override final {
return fsm_;
}
bool is_fsm_none() const {
return fsm_ == FsmState::fsm_none;
}
bool is_fsm_split() const {
return fsm_ == FsmState::fsm_split;
}
bool is_fsm_merge() const {
return fsm_ == FsmState::fsm_merge;
}
ton::ShardIdFull shard() const override final {
return ton::ShardIdFull(blk_);
}
ton::WorkchainId workchain() const {
return blk_.id.workchain;
}
bool contains(const ton::AccountIdPrefixFull& pfx) const {
return ton::shard_contains(shard(), pfx);
}
bool before_split() const override final {
return before_split_;
}
bool before_merge() const override final {
return before_merge_;
}
bool is_disabled() const {
return disabled_;
}
void disable() {
blk_.invalidate();
disabled_ = true;
}
ton::BlockSeqno seqno() const {
return blk_.id.seqno;
}
bool set_reg_mc_seqno(ton::BlockSeqno reg_mc_seqno) {
reg_mc_seqno_ = reg_mc_seqno;
return true;
}
// compares all fields except fsm*, before_merge_, nx_cc_updated_, next_catchain_seqno_, fees_collected_
bool basic_info_equal(const McShardHash& other, bool compare_fees = false, bool compare_reg_seqno = true) const;
void clear_fsm() {
fsm_ = FsmState::fsm_none;
}
void set_fsm(FsmState fsm, ton::UnixTime fsm_utime, ton::UnixTime fsm_interval);
void set_fsm_split(ton::UnixTime fsm_utime, ton::UnixTime fsm_interval) {
set_fsm(FsmState::fsm_split, fsm_utime, fsm_interval);
}
void set_fsm_merge(ton::UnixTime fsm_utime, ton::UnixTime fsm_interval) {
set_fsm(FsmState::fsm_merge, fsm_utime, fsm_interval);
}
bool fsm_equal(const McShardHash& other) const {
return fsm_ == other.fsm_ &&
(is_fsm_none() || (fsm_utime_ == other.fsm_utime_ && fsm_interval_ == other.fsm_interval_));
}
bool pack(vm::CellBuilder& cb) const;
static Ref<McShardHash> unpack(vm::CellSlice& cs, ton::ShardIdFull id);
static Ref<McShardHash> from_block(Ref<vm::Cell> block_root, const ton::FileHash& _fhash, bool init_fees = false);
McShardHash* make_copy() const override {
return new McShardHash(*this);
}
};
struct McShardDescr final : public McShardHash {
Ref<vm::Cell> block_root;
Ref<vm::Cell> state_root;
Ref<vm::Cell> outmsg_root;
std::unique_ptr<vm::AugmentedDictionary> out_msg_queue;
std::shared_ptr<block::MsgProcessedUptoCollection> processed_upto;
McShardDescr(const ton::BlockId& id, ton::LogicalTime start_lt, ton::LogicalTime end_lt, ton::UnixTime gen_utime,
const ton::BlockHash& root_hash, const ton::FileHash& file_hash, CurrencyCollection fees_collected = {},
CurrencyCollection funds_created = {},
ton::BlockSeqno reg_mc_seqno = std::numeric_limits<ton::BlockSeqno>::max(),
ton::BlockSeqno min_ref_mc_seqno = std::numeric_limits<ton::BlockSeqno>::max(),
ton::CatchainSeqno cc_seqno = std::numeric_limits<ton::CatchainSeqno>::max(),
ton::ShardId val_shard = ton::shardIdAll, bool nx_cc_updated = false, bool before_split = false,
bool before_merge = false, bool want_split = false, bool want_merge = false)
: McShardHash(id, start_lt, end_lt, gen_utime, root_hash, file_hash, fees_collected, funds_created, reg_mc_seqno,
min_ref_mc_seqno, cc_seqno, val_shard, nx_cc_updated, before_split, before_merge, want_split,
want_merge) {
}
McShardDescr(const ton::BlockIdExt& blk, ton::LogicalTime start_lt, ton::LogicalTime end_lt)
: McShardHash(blk, start_lt, end_lt) {
}
McShardDescr(const McShardHash& shard_hash) : McShardHash(shard_hash) {
}
McShardDescr(const McShardDescr& other);
McShardDescr(McShardDescr&& other) = default;
McShardDescr& operator=(const McShardDescr& other);
McShardDescr& operator=(McShardDescr&& other) = default;
bool set_queue_root(Ref<vm::Cell> queue_root);
void disable();
static Ref<McShardDescr> from_block(Ref<vm::Cell> block_root, Ref<vm::Cell> state_root, const ton::FileHash& _fhash,
bool init_fees = false);
static Ref<McShardDescr> from_state(ton::BlockIdExt blkid, Ref<vm::Cell> state_root);
};
struct StoragePrices {
ton::UnixTime valid_since{0};
td::uint64 bit_price{0};
td::uint64 cell_price{0};
td::uint64 mc_bit_price{0};
td::uint64 mc_cell_price{0};
StoragePrices() = default;
StoragePrices(ton::UnixTime _valid_since, td::uint64 _bprice, td::uint64 _cprice, td::uint64 _mc_bprice,
td::uint64 _mc_cprice)
: valid_since(_valid_since)
, bit_price(_bprice)
, cell_price(_cprice)
, mc_bit_price(_mc_bprice)
, mc_cell_price(_mc_cprice) {
}
};
struct CatchainValidatorsConfig {
td::uint32 mc_cc_lifetime, shard_cc_lifetime, shard_val_lifetime, shard_val_num;
CatchainValidatorsConfig(td::uint32 mc_cc_lt_, td::uint32 sh_cc_lt_, td::uint32 sh_val_lt_, td::uint32 sh_val_num_)
: mc_cc_lifetime(mc_cc_lt_)
, shard_cc_lifetime(sh_cc_lt_)
, shard_val_lifetime(sh_val_lt_)
, shard_val_num(sh_val_num_) {
}
};
struct WorkchainInfo : public td::CntObject {
ton::WorkchainId workchain{ton::workchainInvalid};
ton::UnixTime enabled_since;
td::uint32 actual_min_split;
td::uint32 min_split, max_split;
bool basic;
bool active;
bool accept_msgs;
int flags;
td::uint32 version;
ton::RootHash zerostate_root_hash;
ton::FileHash zerostate_file_hash;
int min_addr_len, max_addr_len, addr_len_step;
bool is_valid() const {
return workchain != ton::workchainInvalid;
}
bool is_valid_addr_len(int addr_len) const {
return addr_len >= min_addr_len && addr_len <= max_addr_len &&
(addr_len == min_addr_len || addr_len == max_addr_len ||
(addr_len_step > 0 && !((addr_len - min_addr_len) % addr_len_step)));
}
bool unpack(ton::WorkchainId wc, vm::CellSlice& cs);
};
using WorkchainSet = std::map<td::int32, Ref<WorkchainInfo>>;
class ShardConfig {
Ref<vm::Cell> shard_hashes_;
Ref<McShardHash> mc_shard_hash_;
std::unique_ptr<vm::Dictionary> shard_hashes_dict_;
std::set<ton::ShardIdFull> shards_updated_;
bool valid_{false};
public:
ShardConfig() = default;
ShardConfig(const ShardConfig& other);
ShardConfig(ShardConfig&& other) = default;
ShardConfig(Ref<vm::Cell> shard_hashes, Ref<McShardHash> mc_shard_hash = {})
: shard_hashes_(std::move(shard_hashes)), mc_shard_hash_(std::move(mc_shard_hash)) {
init();
}
bool is_valid() const {
return valid_;
}
bool unpack(Ref<vm::Cell> shard_hashes, Ref<McShardHash> mc_shard_hash = {});
bool unpack(Ref<vm::CellSlice> shard_hashes, Ref<McShardHash> mc_shard_hash = {});
Ref<vm::CellSlice> get_root_csr() const;
bool has_workchain(ton::WorkchainId workchain) const;
std::vector<ton::WorkchainId> get_workchains() const;
Ref<McShardHash> get_shard_hash(ton::ShardIdFull id, bool exact = true) const;
bool contains(ton::BlockIdExt blkid) const;
bool get_shard_hash_raw(vm::CellSlice& cs, ton::ShardIdFull id, ton::ShardIdFull& true_id, bool exact = true) const;
ton::LogicalTime get_shard_end_lt(ton::AccountIdPrefixFull acc) const;
ton::LogicalTime get_shard_end_lt_ext(ton::AccountIdPrefixFull acc, ton::ShardIdFull& actual_shard) const;
static bool get_shard_hash_raw_from(vm::Dictionary& shard_hashes_dict, vm::CellSlice& cs, ton::ShardIdFull id,
ton::ShardIdFull& true_id, bool exact = true, Ref<vm::Cell>* leaf = nullptr);
std::vector<ton::BlockId> get_shard_hash_ids(bool skip_mc = false) const;
std::vector<ton::BlockId> get_shard_hash_ids(const std::function<bool(ton::ShardIdFull, bool)>& filter) const;
std::vector<ton::BlockId> get_intersecting_shard_hash_ids(ton::ShardIdFull myself) const;
std::vector<ton::BlockId> get_neighbor_shard_hash_ids(ton::ShardIdFull myself) const;
std::vector<ton::BlockId> get_proper_neighbor_shard_hash_ids(ton::ShardIdFull myself) const;
static std::unique_ptr<vm::Dictionary> extract_shard_hashes_dict(Ref<vm::Cell> mc_state_root);
bool process_shard_hashes(std::function<int(McShardHash&)> func);
bool process_sibling_shard_hashes(std::function<int(McShardHash&, const McShardHash*)> func);
// may become non-static const in the future
static bool is_neighbor(ton::ShardIdFull x, ton::ShardIdFull y);
Ref<McShardHash> get_mc_hash() const {
return mc_shard_hash_;
}
void set_mc_hash(Ref<McShardHash> mc_shard_hash) {
mc_shard_hash_ = std::move(mc_shard_hash);
}
ton::CatchainSeqno get_shard_cc_seqno(ton::ShardIdFull shard) const;
block::compute_shard_end_lt_func_t get_compute_shard_end_lt_func() const {
return std::bind(&ShardConfig::get_shard_end_lt, *this, std::placeholders::_1);
}
bool new_workchain(ton::WorkchainId workchain, ton::BlockSeqno reg_mc_seqno, const ton::RootHash& zerostate_root_hash,
const ton::FileHash& zerostate_file_hash);
td::Result<bool> update_shard_block_info(Ref<McShardHash> new_info, const std::vector<ton::BlockIdExt>& old_blkids);
td::Result<bool> update_shard_block_info2(Ref<McShardHash> new_info1, Ref<McShardHash> new_info2,
const std::vector<ton::BlockIdExt>& old_blkids);
td::Result<bool> may_update_shard_block_info(Ref<McShardHash> new_info,
const std::vector<ton::BlockIdExt>& old_blkids,
ton::LogicalTime lt_limit = std::numeric_limits<ton::LogicalTime>::max(),
Ref<McShardHash>* ancestor = nullptr) const;
private:
bool init();
bool do_update_shard_info(Ref<McShardHash> new_info);
bool do_update_shard_info2(Ref<McShardHash> new_info1, Ref<McShardHash> new_info2);
bool set_shard_info(ton::ShardIdFull shard, Ref<vm::Cell> value);
};
class Config {
enum {
default_mc_catchain_lifetime = 200,
default_shard_catchain_lifetime = 200,
default_shard_validators_lifetime = 3000,
default_shard_validators_num = 7
};
public:
enum { needValidatorSet = 16, needSpecialSmc = 32, needWorkchainInfo = 256 };
int mode{0};
ton::BlockIdExt block_id;
private:
td::BitArray<256> config_addr;
Ref<vm::Cell> config_root;
std::unique_ptr<vm::Dictionary> config_dict;
std::unique_ptr<ValidatorSet> cur_validators_;
std::unique_ptr<vm::Dictionary> workchains_dict_;
WorkchainSet workchains_;
protected:
std::unique_ptr<vm::Dictionary> special_smc_dict;
public:
static constexpr ton::LogicalTime get_lt_align() {
return 1000000;
}
static constexpr ton::LogicalTime get_max_lt_growth() {
return 10 * get_lt_align() - 1;
}
Ref<vm::Cell> get_config_param(int idx) const;
Ref<vm::Cell> get_config_param(int idx, int idx2) const;
Ref<vm::Cell> operator[](int idx) const {
return get_config_param(idx);
}
Ref<vm::Cell> get_root_cell() const {
return config_root;
}
bool is_masterchain() const {
return block_id.is_masterchain();
}
bool set_block_id_ext(const ton::BlockIdExt& block_id_ext);
td::Result<std::vector<ton::StdSmcAddress>> get_special_smartcontracts(bool without_config = false) const;
bool is_special_smartcontract(const ton::StdSmcAddress& addr) const;
static td::Result<std::unique_ptr<ValidatorSet>> unpack_validator_set(Ref<vm::Cell> valset_root);
td::Result<std::vector<StoragePrices>> get_storage_prices() const;
static CatchainValidatorsConfig unpack_catchain_validators_config(Ref<vm::Cell> cell);
CatchainValidatorsConfig get_catchain_validators_config() const;
td::Status visit_validator_params() const;
td::Result<std::unique_ptr<BlockLimits>> get_block_limits(bool is_masterchain = false) const;
auto get_mc_block_limits() const {
return get_block_limits(true);
}
static td::Result<std::pair<WorkchainSet, std::unique_ptr<vm::Dictionary>>> unpack_workchain_list_ext(
Ref<vm::Cell> cell);
static td::Result<WorkchainSet> unpack_workchain_list(Ref<vm::Cell> cell);
const WorkchainSet& get_workchain_list() const {
return workchains_;
}
const ValidatorSet* get_cur_validator_set() const {
return cur_validators_.get();
}
ton::ValidatorSessionConfig get_consensus_config() const;
bool foreach_config_param(std::function<bool(int, Ref<vm::Cell>)> scan_func) const;
Ref<WorkchainInfo> get_workchain_info(ton::WorkchainId workchain_id) const;
std::vector<ton::ValidatorDescr> compute_validator_set(ton::ShardIdFull shard, const block::ValidatorSet& vset,
ton::UnixTime time, ton::CatchainSeqno cc_seqno) const;
std::vector<ton::ValidatorDescr> compute_validator_set(ton::ShardIdFull shard, ton::UnixTime time,
ton::CatchainSeqno cc_seqno) const;
std::vector<ton::ValidatorDescr> compute_total_validator_set(int next) const;
static std::vector<ton::ValidatorDescr> do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf,
ton::ShardIdFull shard,
const block::ValidatorSet& vset, ton::UnixTime time,
ton::CatchainSeqno cc_seqno);
static td::Result<std::unique_ptr<Config>> unpack_config(Ref<vm::Cell> config_root,
const td::Bits256& config_addr = td::Bits256::zero(),
int mode = 0);
static td::Result<std::unique_ptr<Config>> unpack_config(Ref<vm::CellSlice> config_csr, int mode = 0);
static td::Result<std::unique_ptr<Config>> extract_from_state(Ref<vm::Cell> mc_state_root, int mode = 0);
static td::Result<std::unique_ptr<Config>> extract_from_key_block(Ref<vm::Cell> key_block_root, int mode = 0);
protected:
Config(int _mode) : mode(_mode) {
config_addr.set_zero();
}
Config(Ref<vm::Cell> config_root, const td::Bits256& config_addr = td::Bits256::zero(), int _mode = 0);
td::Status unpack_wrapped(Ref<vm::CellSlice> config_csr);
td::Status unpack(Ref<vm::CellSlice> config_csr);
td::Status unpack_wrapped();
td::Status unpack();
};
class ConfigInfo : public Config, public ShardConfig {
public:
enum {
needStateRoot = 1,
needLibraries = 2,
needStateExtraRoot = 4,
needShardHashes = 8,
needAccountsRoot = 64,
needPrevBlocks = 128
};
int vert_seqno{-1};
int global_id_{0};
ton::UnixTime utime{0};
ton::LogicalTime lt{0};
ton::BlockSeqno min_ref_mc_seqno_{std::numeric_limits<ton::BlockSeqno>::max()};
ton::CatchainSeqno cc_seqno_{std::numeric_limits<ton::CatchainSeqno>::max()};
int shard_cc_updated{-1};
bool nx_cc_updated;
bool is_key_state_{false};
private:
Ref<vm::Cell> state_root;
Ref<vm::Cell> lib_root_;
Ref<vm::Cell> state_extra_root_;
Ref<vm::CellSlice> accounts_root;
ton::ZeroStateIdExt zerostate_id_;
ton::BlockIdExt last_key_block_;
ton::LogicalTime last_key_block_lt_;
Ref<vm::Cell> shard_hashes;
std::unique_ptr<vm::Dictionary> shard_hashes_dict;
std::unique_ptr<vm::AugmentedDictionary> accounts_dict;
std::unique_ptr<vm::AugmentedDictionary> prev_blocks_dict_;
std::unique_ptr<vm::Dictionary> libraries_dict_;
public:
bool set_block_id_ext(const ton::BlockIdExt& block_id_ext);
bool rotated_all_shards() const {
return nx_cc_updated;
}
int get_global_blockchain_id() const {
return global_id_;
}
ton::ZeroStateIdExt get_zerostate_id() const {
return zerostate_id_;
}
Ref<vm::Cell> lookup_library(const ton::Bits256& root_hash) const {
return lookup_library(root_hash.bits());
}
Ref<vm::Cell> lookup_library(td::ConstBitPtr root_hash) const;
Ref<vm::Cell> get_libraries_root() const {
return lib_root_;
}
bool is_key_state() const {
return is_key_state_;
}
Ref<vm::Cell> get_state_extra_root() const {
return state_extra_root_;
}
ton::CatchainSeqno get_shard_cc_seqno(ton::ShardIdFull shard) const;
bool get_last_key_block(ton::BlockIdExt& blkid, ton::LogicalTime& blklt, bool strict = false) const;
bool get_old_mc_block_id(ton::BlockSeqno seqno, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const;
bool check_old_mc_block_id(const ton::BlockIdExt& blkid, bool strict = false) const;
// returns block with min seqno and req_lt <= block.end_lt
bool get_mc_block_by_lt(ton::LogicalTime lt, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const;
bool get_prev_key_block(ton::BlockSeqno req_seqno, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const;
bool get_next_key_block(ton::BlockSeqno req_seqno, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const;
td::Result<std::vector<std::pair<ton::StdSmcAddress, int>>> get_special_ticktock_smartcontracts(
int tick_tock = 3) const;
int get_smc_tick_tock(td::ConstBitPtr smc_addr) const;
std::unique_ptr<vm::AugmentedDictionary> create_accounts_dict() const;
const vm::AugmentedDictionary& get_accounts_dict() const;
std::vector<ton::ValidatorDescr> compute_validator_set_cc(ton::ShardIdFull shard, const block::ValidatorSet& vset,
ton::UnixTime time,
ton::CatchainSeqno* cc_seqno_delta = nullptr) const;
std::vector<ton::ValidatorDescr> compute_validator_set_cc(ton::ShardIdFull shard, ton::UnixTime time,
ton::CatchainSeqno* cc_seqno_delta = nullptr) const;
static td::Result<std::unique_ptr<ConfigInfo>> extract_config(std::shared_ptr<vm::StaticBagOfCellsDb> static_boc,
int mode = 0);
static td::Result<std::unique_ptr<ConfigInfo>> extract_config(Ref<vm::Cell> mc_state_root, int mode = 0);
private:
ConfigInfo(Ref<vm::Cell> mc_state_root, int _mode = 0);
td::Status unpack_wrapped();
td::Status unpack();
void reset_mc_hash();
void cleanup();
};
} // namespace block

View file

@ -0,0 +1,221 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "output-queue-merger.h"
namespace block {
/*
*
* OUTPUT QUEUE MERGER
*
*/
bool OutputQueueMerger::MsgKeyValue::operator<(const MsgKeyValue& other) const {
return lt < other.lt ||
(lt == other.lt && td::bitstring::bits_memcmp(key.cbits() + 96, other.key.cbits() + 96, 256) < 0);
}
bool OutputQueueMerger::MsgKeyValue::less(const std::unique_ptr<MsgKeyValue>& he1,
const std::unique_ptr<MsgKeyValue>& he2) {
return *he1 < *he2;
}
bool OutputQueueMerger::MsgKeyValue::greater(const std::unique_ptr<MsgKeyValue>& he1,
const std::unique_ptr<MsgKeyValue>& he2) {
return *he2 < *he1;
}
OutputQueueMerger::MsgKeyValue::MsgKeyValue(td::ConstBitPtr key_pfx, int key_pfx_len, int _src, Ref<vm::Cell> node)
: source(_src) {
unpack_node(key_pfx, key_pfx_len, std::move(node));
}
OutputQueueMerger::MsgKeyValue::MsgKeyValue(int _src, Ref<vm::Cell> node) : source(_src) {
unpack_node(td::ConstBitPtr{nullptr}, 0, std::move(node));
}
bool OutputQueueMerger::MsgKeyValue::invalidate() {
msg.clear();
lt = 0;
source = -1;
return false;
}
ton::LogicalTime OutputQueueMerger::MsgKeyValue::get_node_lt(Ref<vm::Cell> node, int key_pfx_len) {
if (node.is_null() || (unsigned)key_pfx_len > (unsigned)max_key_len) {
return std::numeric_limits<td::uint64>::max();
}
vm::dict::LabelParser label{std::move(node), max_key_len - key_pfx_len, vm::dict::LabelParser::chk_size};
if (!label.is_valid()) {
return std::numeric_limits<td::uint64>::max();
}
label.skip_label();
return label.remainder->prefetch_ulong(64);
}
bool OutputQueueMerger::MsgKeyValue::unpack_node(td::ConstBitPtr key_pfx, int key_pfx_len, Ref<vm::Cell> node) {
if (node.is_null() || (unsigned)key_pfx_len >= (unsigned)max_key_len) {
return invalidate();
}
if (!key_pfx.is_null()) {
td::bitstring::bits_memcpy(key.bits(), key_pfx, key_pfx_len);
}
vm::dict::LabelParser label{std::move(node), max_key_len - key_pfx_len, vm::dict::LabelParser::chk_size};
if (!label.is_valid()) {
return invalidate();
}
label.extract_label_to(key.bits() + key_pfx_len);
key_len = key_pfx_len + label.l_bits;
msg = std::move(label.remainder);
if (!msg.write().fetch_uint_to(64, lt)) {
return invalidate();
}
if (is_fork() && msg->size_ext() != 0x20000) {
return invalidate();
}
return true;
}
bool OutputQueueMerger::MsgKeyValue::replace_with_child(bool child_idx) {
if (!is_fork() || msg.is_null() || msg->size_ext() != 0x20000) {
return false;
}
key[key_len] = child_idx;
return unpack_node(td::ConstBitPtr{nullptr}, key_len + 1, msg->prefetch_ref(child_idx));
}
bool OutputQueueMerger::MsgKeyValue::replace_by_prefix(td::ConstBitPtr req_pfx, int req_pfx_len) {
do {
if (td::bitstring::bits_memcmp(req_pfx, key.cbits(), std::min(req_pfx_len, key_len))) {
return false;
}
if (key_len >= req_pfx_len) {
return true;
}
} while (replace_with_child(req_pfx[key_len]));
return false;
}
bool OutputQueueMerger::MsgKeyValue::split(MsgKeyValue& second) {
if (!is_fork() || msg.is_null()) {
return false;
}
unsigned long long keep_lt = lt;
unsigned long long left_lt = get_node_lt(msg->prefetch_ref(0), key_len + 1);
bool sw = (left_lt == lt);
second.source = source;
key[key_len] = sw;
if (!second.unpack_node(key.cbits(), key_len + 1, msg->prefetch_ref(sw))) {
return false;
}
key[key_len] = 1 - sw;
if (!unpack_node(td::ConstBitPtr{nullptr}, key_len + 1, msg->prefetch_ref(1 - sw))) {
return false;
}
if (lt != keep_lt || second.lt < keep_lt) {
return false;
}
return true;
}
bool OutputQueueMerger::add_root(int src, Ref<vm::Cell> outmsg_root) {
if (outmsg_root.is_null()) {
return true;
}
//block::gen::HashmapAug{352, block::gen::t_EnqueuedMsg, block::gen::t_uint64}.print_ref(std::cerr, outmsg_root);
auto kv = std::make_unique<MsgKeyValue>(src, std::move(outmsg_root));
if (kv->replace_by_prefix(common_pfx.cbits(), common_pfx_len)) {
heap.push_back(std::move(kv));
}
return true;
}
OutputQueueMerger::OutputQueueMerger(ton::ShardIdFull _queue_for, std::vector<block::McShardDescr> _neighbors)
: queue_for(_queue_for), neighbors(std::move(_neighbors)), eof(false), failed(false) {
init();
}
void OutputQueueMerger::init() {
common_pfx.bits().store_int(queue_for.workchain, 32);
int l = queue_for.pfx_len();
td::bitstring::bits_store_long_top(common_pfx.bits() + 32, queue_for.shard, l);
common_pfx_len = 32 + l;
int i = 0;
for (block::McShardDescr& neighbor : neighbors) {
if (!neighbor.is_disabled()) {
LOG(DEBUG) << "adding " << (neighbor.outmsg_root.is_null() ? "" : "non-") << "empty output queue for neighbor #"
<< i << " (" << neighbor.blk_.to_str() << ")";
add_root(i++, neighbor.outmsg_root);
} else {
LOG(DEBUG) << "skipping output queue for disabled neighbor #" << i;
i++;
}
}
std::make_heap(heap.begin(), heap.end(), MsgKeyValue::greater);
eof = heap.empty();
if (!eof) {
load();
}
}
OutputQueueMerger::MsgKeyValue* OutputQueueMerger::cur() {
return eof ? nullptr : msg_list.at(pos).get();
}
std::unique_ptr<OutputQueueMerger::MsgKeyValue> OutputQueueMerger::extract_cur() {
return eof ? std::unique_ptr<MsgKeyValue>{} : std::move(msg_list.at(pos));
}
bool OutputQueueMerger::next() {
if (eof) {
return false;
} else if (++pos < msg_list.size() || load()) {
return true;
} else {
eof = true;
return false;
}
}
bool OutputQueueMerger::load() {
if (heap.empty() || failed) {
return false;
}
unsigned long long lt = heap[0]->lt;
std::size_t orig_size = msg_list.size();
do {
while (heap[0]->is_fork()) {
auto other = std::make_unique<MsgKeyValue>();
if (!heap[0]->split(*other)) {
failed = true;
return false;
}
heap.push_back(std::move(other));
std::push_heap(heap.begin(), heap.end(), MsgKeyValue::greater);
}
assert(heap[0]->lt == lt);
std::pop_heap(heap.begin(), heap.end(), MsgKeyValue::greater);
msg_list.push_back(std::move(heap.back()));
heap.pop_back();
} while (!heap.empty() && heap[0]->lt <= lt);
std::sort(msg_list.begin() + orig_size, msg_list.end(), MsgKeyValue::less);
return true;
}
} // namespace block

View file

@ -0,0 +1,80 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "ton/ton-types.h"
#include "vm/cells/CellSlice.h"
#include "block/mc-config.h"
namespace block {
using td::Ref;
struct OutputQueueMerger {
struct MsgKeyValue {
static constexpr int max_key_len = 32 + 64 + 256;
Ref<vm::CellSlice> msg;
unsigned long long lt;
int source;
int key_len{0};
td::BitArray<max_key_len> key;
MsgKeyValue() = default;
MsgKeyValue(int src, Ref<vm::Cell> node);
MsgKeyValue(td::ConstBitPtr key_pfx, int key_pfx_len, int src, Ref<vm::Cell> node);
bool operator<(const MsgKeyValue& other) const;
bool is_fork() const {
return key_len < max_key_len;
}
bool invalidate();
static bool less(const std::unique_ptr<MsgKeyValue>& he1, const std::unique_ptr<MsgKeyValue>& he2);
static bool greater(const std::unique_ptr<MsgKeyValue>& he1, const std::unique_ptr<MsgKeyValue>& he2);
protected:
friend struct OutputQueueMerger;
static ton::LogicalTime get_node_lt(Ref<vm::Cell> node, int key_pfx_len);
bool replace_with_child(bool child_idx);
bool replace_by_prefix(td::ConstBitPtr req_pfx, int req_pfx_len);
bool unpack_node(td::ConstBitPtr key_pfx, int key_pfx_len, Ref<vm::Cell> node);
bool split(MsgKeyValue& second);
};
//
ton::ShardIdFull queue_for;
std::vector<std::unique_ptr<MsgKeyValue>> msg_list;
std::vector<block::McShardDescr> neighbors;
public:
OutputQueueMerger(ton::ShardIdFull _queue_for, std::vector<block::McShardDescr> _neighbors);
bool is_eof() const {
return eof;
}
MsgKeyValue* cur();
std::unique_ptr<MsgKeyValue> extract_cur();
bool next();
private:
td::BitArray<32 + 64> common_pfx;
int common_pfx_len;
std::vector<std::unique_ptr<MsgKeyValue>> heap;
std::size_t pos{0};
bool eof;
bool failed;
void init();
bool add_root(int src, Ref<vm::Cell> outmsg_root);
bool load();
};
} // namespace block

248
crypto/block/test-block.cpp Normal file
View file

@ -0,0 +1,248 @@
/*
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-2019 Telegram Systems LLP
*/
#include "block/block.h"
#include "vm/boc.h"
#include <iostream>
#include "block-db.h"
#include "block-auto.h"
#include "block-parse.h"
#include "vm/cp0.h"
#include <getopt.h>
using td::Ref;
int verbosity;
struct IntError {
std::string err_msg;
IntError(std::string _msg) : err_msg(_msg) {
}
IntError(const char* _msg) : err_msg(_msg) {
}
};
td::Ref<vm::Cell> load_boc(std::string filename) {
std::cerr << "loading bag-of-cell file " << filename << std::endl;
auto bytes_res = block::load_binary_file(filename);
if (bytes_res.is_error()) {
throw IntError{PSTRING() << "cannot load file `" << filename << "` : " << bytes_res.move_as_error()};
}
vm::BagOfCells boc;
auto res = boc.deserialize(bytes_res.move_as_ok());
if (res.is_error()) {
throw IntError{PSTRING() << "cannot deserialize bag-of-cells " << res.move_as_error()};
}
if (res.move_as_ok() <= 0 || boc.get_root_cell().is_null()) {
throw IntError{"cannot deserialize bag-of-cells "};
}
return boc.get_root_cell();
}
void test1() {
block::ShardId id{ton::masterchainId}, id2{ton::basechainId, 0x11efULL << 48};
std::cout << '[' << id << "][" << id2 << ']' << std::endl;
vm::CellBuilder cb;
cb << id << id2;
std::cout << "ShardIdent.pack() = " << block::tlb::t_ShardIdent.pack(cb, {12, 3, 0x3aeULL << 52}) << std::endl;
std::cout << cb << std::endl;
auto cref = cb.finalize();
td::Ref<vm::CellSlice> cs{true, cref}, cs2;
block::ShardId id3{cs.write()}, id4, id5;
cs >> id4 >> id5;
std::cout << '[' << id3 << "][" << id4 << "][" << id5 << ']' << std::endl;
vm::CellSlice csl{std::move(cref)};
std::cout << "ShardIdent.get_size() = " << block::tlb::t_ShardIdent.get_size(csl) << std::endl;
std::cout << "MsgAddress.get_size() = " << block::tlb::t_MsgAddress.get_size(csl) << std::endl;
std::cout << "Grams.get_size() = " << block::tlb::t_Grams.get_size(csl) << std::endl;
std::cout << "Grams.as_integer() = " << block::tlb::t_Grams.as_integer(csl) << std::endl;
(csl + 8).print_rec(std::cout);
std::cout << "Grams.get_size() = " << block::tlb::t_Grams.get_size(csl + 8) << std::endl;
std::cout << "Grams.as_integer() = " << block::tlb::t_Grams.as_integer(csl + 8) << std::endl;
vm::CellSlice csl2{csl};
block::gen::ShardIdent::Record sh_id;
for (int i = 0; i < 3; i++) {
std::cout << csl2 << std::endl;
bool ok = tlb::unpack(csl2, sh_id);
std::cout << "block::gen::ShardIdent.unpack() = " << ok << std::endl;
if (ok) {
std::cout << " (shard_ident shard_pfx_bits:" << sh_id.shard_pfx_bits << " workchain_id:" << sh_id.workchain_id
<< " shard_prefix:" << std::hex << sh_id.shard_prefix << std::dec << ")" << std::endl;
}
}
block::tlb::ShardIdent::Record shard_id;
for (int i = 0; i < 3; i++) {
std::cout << "ShardIdent.validate() = " << block::tlb::t_ShardIdent.validate(csl) << std::endl;
csl.print_rec(std::cerr);
csl.dump(std::cerr, 7);
std::cout << "ShardIdent.unpack() = " << block::tlb::t_ShardIdent.unpack(csl, shard_id) << std::endl;
if (shard_id.is_valid()) {
std::cout << "shard_pfx_bits:" << shard_id.shard_pfx_bits << " workchain_id:" << shard_id.workchain_id
<< " shard_prefix:" << shard_id.shard_prefix << std::endl;
}
}
std::cout << "ShardIdent.skip_validate() = " << block::tlb::t_ShardIdent.validate_skip(csl) << std::endl;
std::cout << "ShardIdent.skip_validate() = " << block::tlb::t_ShardIdent.validate_skip(csl) << std::endl;
std::cout << "ShardIdent.skip_validate() = " << block::tlb::t_ShardIdent.validate_skip(csl) << std::endl;
using namespace td::literals;
std::cout << "Grams.store_intval(239) = " << block::tlb::t_Grams.store_integer_value(cb, "239"_i256) << std::endl;
std::cout << "Grams.store_intval(17239) = " << block::tlb::t_Grams.store_integer_value(cb, "17239"_i256) << std::endl;
std::cout << "Grams.store_intval(-17) = " << block::tlb::t_Grams.store_integer_value(cb, "-17"_i256) << std::endl;
std::cout << "Grams.store_intval(0) = " << block::tlb::t_Grams.store_integer_value(cb, "0"_i256) << std::endl;
std::cout << cb << std::endl;
cs = td::Ref<vm::CellSlice>{true, cb.finalize()};
std::cout << "Grams.store_intval(666) = " << block::tlb::t_Grams.store_integer_value(cb, "666"_i256) << std::endl;
std::cout << cb << std::endl;
cs2 = td::Ref<vm::CellSlice>{true, cb.finalize()};
std::cout << "Grams.validate(cs) = " << block::tlb::t_Grams.validate(*cs) << std::endl;
std::cout << "Grams.validate(cs2) = " << block::tlb::t_Grams.validate(*cs2) << std::endl;
//
block::gen::SplitMergeInfo::Record data;
block::gen::Grams::Record data2;
std::cout << "block::gen::Grams.validate(cs) = " << block::gen::t_Grams.validate(*cs) << std::endl;
std::cout << "block::gen::Grams.validate(cs2) = " << block::gen::t_Grams.validate(*cs2) << std::endl;
std::cout << "[cs = " << cs << "]" << std::endl;
bool ok = tlb::csr_unpack_inexact(cs, data);
std::cout << "block::gen::SplitMergeInfo.unpack(cs, data) = " << ok << std::endl;
if (ok) {
std::cout << " cur_shard_pfx_len = " << data.cur_shard_pfx_len << "; acc_split_depth = " << data.acc_split_depth
<< "; this_addr = " << data.this_addr << "; sibling_addr = " << data.sibling_addr << std::endl;
}
ok = tlb::csr_unpack_inexact(cs, data2);
std::cout << "block::gen::Grams.unpack(cs, data2) = " << ok << std::endl;
if (ok) {
std::cout << " amount = " << data2.amount << std::endl;
block::gen::VarUInteger::Record data3;
ok = tlb::csr_type_unpack(data2.amount, block::gen::t_VarUInteger_16, data3);
std::cout << " block::gen::VarUInteger16.unpack(amount, data3) = " << ok << std::endl;
if (ok) {
std::cout << " len = " << data3.len << "; value = " << data3.value << std::endl;
vm::CellBuilder cb;
std::cout << " block::gen::VarUInteger16.pack(cb, data3) = "
<< tlb::type_pack(cb, block::gen::t_VarUInteger_16, data3) << std::endl;
std::cout << " cb = " << cb.finalize() << std::endl;
}
}
/*
{
vm::CellBuilder cb;
td::BitArray<256> hash;
std::memset(hash.data(), 0x69, 32);
bool ok = tlb::pack(
cb, block::gen::Test::Record{1000000000000, {170239, -888, {239017, "1000000000000000000"_ri256}, hash}, 17});
std::cout << " block::gen::Test::pack(cb, {1000000000000, ...}) = " << ok << std::endl;
std::cout << " cb = " << cb << std::endl;
auto cell = cb.finalize();
vm::CellSlice cs{cell};
cs.print_rec(std::cout);
block::gen::Test::Record data;
std::cout << " block::gen::Test::validate_ref(cell) = " << block::gen::t_Test.validate_ref(cell) << std::endl;
ok = tlb::unpack(cs, data);
std::cout << " block::gen::Test::unpack(cs, data) = " << ok << std::endl;
if (ok) {
std::cout << "a:" << data.a << " b:" << data.r1.b << " c:" << data.r1.c << " d:" << data.r1.r1.d
<< " e:" << data.r1.r1.e << " f:" << data.r1.f << " g:" << data.g << std::endl;
}
std::cout << " block::gen::Test::print_ref(cell) = ";
block::gen::t_Test.print_ref(std::cout, cell, 2);
block::gen::t_CurrencyCollection.print_ref(std::cout, cell, 2);
std::cout << std::endl;
}
*/
std::cout << "Grams.add_values() = " << block::tlb::t_Grams.add_values(cb, cs.write(), cs2.write()) << std::endl;
std::cout << cb << std::endl;
std::cout << "block::gen::t_HashmapAug_64_...print_type() = "
<< block::gen::t_HashmapAug_64_Ref_Transaction_CurrencyCollection << std::endl;
}
void test2(vm::CellSlice& cs) {
std::cout << "Bool.validate() = " << block::tlb::t_Bool.validate(cs) << std::endl;
std::cout << "UInt16.validate() = " << block::tlb::t_uint16.validate(cs) << std::endl;
std::cout << "HashmapE(32,UInt16).validate() = " << block::tlb::HashmapE(32, block::tlb::t_uint16).validate(cs)
<< std::endl;
std::cout << "block::gen::HashmapE(32,UInt16).validate() = "
<< block::gen::HashmapE{32, block::gen::t_uint16}.validate(cs) << std::endl;
}
void usage() {
std::cout << "usage: test-block [<boc-file>]\n\tor test-block -h\n";
std::exit(2);
}
int main(int argc, char* const argv[]) {
int i;
int new_verbosity_level = VERBOSITY_NAME(INFO);
auto zerostate = std::make_unique<block::ZerostateInfo>();
while ((i = getopt(argc, argv, "hv:")) != -1) {
switch (i) {
case 'v':
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
break;
case 'h':
usage();
std::exit(2);
default:
usage();
std::exit(2);
}
}
SET_VERBOSITY_LEVEL(new_verbosity_level);
try {
bool done = false;
while (optind < argc) {
auto boc = load_boc(argv[optind++]);
if (boc.is_null()) {
std::cerr << "(invalid boc)" << std::endl;
std::exit(2);
} else {
done = true;
vm::CellSlice cs{vm::NoVm(), boc};
cs.print_rec(std::cout);
std::cout << std::endl;
block::gen::t_Block.print_ref(std::cout, boc);
std::cout << std::endl;
if (!block::gen::t_Block.validate_ref(boc)) {
std::cout << "(invalid Block)" << std::endl;
} else {
std::cout << "(valid Block)" << std::endl;
}
}
}
if (!done) {
test1();
}
} catch (IntError& err) {
std::cerr << "caught internal error " << err.err_msg << std::endl;
return 1;
} catch (vm::VmError& err) {
std::cerr << "caught vm error " << err.get_msg() << std::endl;
return 1;
}
return 0;
}

2089
crypto/block/transaction.cpp Normal file

File diff suppressed because it is too large Load diff

401
crypto/block/transaction.h Normal file
View file

@ -0,0 +1,401 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "common/refcnt.hpp"
#include "common/refint.h"
#include "vm/cells.h"
#include "vm/cellslice.h"
#include "vm/dict.h"
#include "vm/boc.h"
#include <ostream>
#include "tl/tlblib.hpp"
#include "td/utils/bits.h"
#include "ton/ton-types.h"
#include "block/block.h"
#include "block/mc-config.h"
namespace block {
using td::Ref;
using LtCellRef = std::pair<ton::LogicalTime, Ref<vm::Cell>>;
struct Account;
struct Transaction;
struct CollatorError {
std::string msg;
CollatorError(std::string _msg) : msg(_msg) {
}
CollatorError(const char* _msg) : msg(_msg) {
}
std::string get_msg() const {
return msg;
}
};
static inline bool operator<(const LtCellRef& a, const LtCellRef& b) {
return a.first < b.first;
}
struct LtCellCompare {
bool operator()(const LtCellRef& a, const LtCellRef& b) {
return a.first < b.first;
}
};
struct NewOutMsg {
ton::LogicalTime lt;
Ref<vm::Cell> msg;
Ref<vm::Cell> trans;
NewOutMsg(ton::LogicalTime _lt, Ref<vm::Cell> _msg, Ref<vm::Cell> _trans)
: lt(_lt), msg(std::move(_msg)), trans(std::move(_trans)) {
}
bool operator<(const NewOutMsg& other) const & {
return lt < other.lt || (lt == other.lt && msg->get_hash() < other.msg->get_hash());
}
bool operator>(const NewOutMsg& other) const & {
return lt > other.lt || (lt == other.lt && other.msg->get_hash() < msg->get_hash());
}
};
struct StoragePhaseConfig {
const std::vector<block::StoragePrices>* pricing{nullptr};
td::RefInt256 freeze_due_limit;
td::RefInt256 delete_due_limit;
StoragePhaseConfig() = default;
StoragePhaseConfig(const std::vector<block::StoragePrices>* _pricing, td::RefInt256 freeze_limit = {},
td::RefInt256 delete_limit = {})
: pricing(_pricing), freeze_due_limit(freeze_limit), delete_due_limit(delete_limit) {
}
};
struct StoragePhase {
td::RefInt256 fees_collected;
td::RefInt256 fees_due;
ton::UnixTime last_paid_updated;
bool frozen{false};
bool deleted{false};
bool is_special{false};
};
struct ComputePhaseConfig {
td::uint64 gas_price;
td::uint64 gas_limit;
td::uint64 gas_credit;
static constexpr td::uint64 gas_infty = (1ULL << 63) - 1;
td::RefInt256 gas_price256;
td::RefInt256 max_gas_threshold;
std::unique_ptr<vm::Dictionary> libraries;
Ref<vm::Cell> global_config;
td::BitArray<256> block_rand_seed;
ComputePhaseConfig(td::uint64 _gas_price = 0, td::uint64 _gas_limit = 0, td::uint64 _gas_credit = 0)
: gas_price(_gas_price), gas_limit(_gas_limit), gas_credit(_gas_credit) {
compute_threshold();
}
void compute_threshold();
td::uint64 gas_bought_for(td::RefInt256 nanograms) const;
td::RefInt256 compute_gas_price(td::uint64 gas_used) const;
void set_gas_price(td::uint64 _gas_price) {
gas_price = _gas_price;
compute_threshold();
}
Ref<vm::Cell> lookup_library(td::ConstBitPtr key) const;
Ref<vm::Cell> lookup_library(const td::Bits256& key) const {
return lookup_library(key.bits());
}
Ref<vm::Cell> get_lib_root() const {
return libraries ? libraries->get_root_cell() : Ref<vm::Cell>{};
}
};
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms
// ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor)/2^16) nanograms
// bits in the root cell of a message are not included in msg.bits (lump_price pays for them)
struct MsgPrices {
td::uint64 lump_price;
td::uint64 bit_price;
td::uint64 cell_price;
td::uint32 ihr_factor;
td::uint32 first_frac;
td::uint32 next_frac;
td::uint64 compute_fwd_fees(td::uint64 cells, td::uint64 bits) const;
std::pair<td::uint64, td::uint64> compute_fwd_ihr_fees(td::uint64 cells, td::uint64 bits,
bool ihr_disabled = false) const;
MsgPrices() = default;
MsgPrices(td::uint64 lump, td::uint64 bitp, td::uint64 cellp, td::uint32 ihrf, td::uint32 firstf, td::uint32 nextf)
: lump_price(lump), bit_price(bitp), cell_price(cellp), ihr_factor(ihrf), first_frac(firstf), next_frac(nextf) {
}
td::RefInt256 get_first_part(td::RefInt256 total) const;
td::uint64 get_first_part(td::uint64 total) const;
td::RefInt256 get_next_part(td::RefInt256 total) const;
};
struct ActionPhaseConfig {
int max_actions{255};
MsgPrices fwd_std;
MsgPrices fwd_mc; // from/to masterchain
const WorkchainSet* workchains{nullptr};
const MsgPrices& fetch_msg_prices(bool is_masterchain) const {
return is_masterchain ? fwd_mc : fwd_std;
}
};
struct CreditPhase {
td::RefInt256 due_fees_collected;
block::CurrencyCollection credit;
// td::RefInt256 credit;
// Ref<vm::Cell> credit_extra;
};
struct ComputePhase {
enum { sk_none, sk_no_state, sk_bad_state, sk_no_gas };
int skip_reason{sk_none};
bool success{false};
bool msg_state_used{false};
bool account_activated{false};
bool out_of_gas{false};
bool accepted{false};
td::RefInt256 gas_fees;
td::uint64 gas_used, gas_max, gas_limit, gas_credit;
int mode;
int exit_code;
int exit_arg;
int vm_steps;
ton::Bits256 vm_init_state_hash, vm_final_state_hash;
Ref<vm::Cell> in_msg;
Ref<vm::Cell> new_data;
Ref<vm::Cell> actions;
};
struct ActionPhase {
bool success{false};
bool valid{false};
bool no_funds{false};
bool code_changed{false};
bool action_list_invalid{false};
bool acc_delete_req{false};
enum { acst_unchanged = 0, acst_frozen = 2, acst_deleted = 3 };
int acc_status_change{acst_unchanged};
td::RefInt256 total_fwd_fees; // all fees debited from the account
td::RefInt256 total_action_fees; // fees credited to validators in this action phase
int result_code;
int result_arg;
int tot_actions;
int spec_actions;
int skipped_actions;
int msgs_created;
Ref<vm::Cell> new_code;
td::BitArray<256> action_list_hash;
block::CurrencyCollection remaining_balance, reserved_balance;
// td::RefInt256 remaining_balance;
// Ref<vm::Cell> remaining_extra;
// td::RefInt256 reserved_balance;
// Ref<vm::Cell> reserved_extra;
std::vector<Ref<vm::Cell>> action_list; // processed in reverse order
std::vector<Ref<vm::Cell>> out_msgs;
ton::LogicalTime end_lt;
unsigned long long tot_msg_bits{0}, tot_msg_cells{0};
};
struct BouncePhase {
bool ok{false};
bool nofunds{false};
unsigned long long msg_bits, msg_cells;
unsigned long long fwd_fees, fwd_fees_collected;
Ref<vm::Cell> out_msg;
};
struct Account {
enum { acc_nonexist = 0, acc_uninit = 1, acc_frozen = 2, acc_active = 3, acc_deleted = 4 };
int status{acc_nonexist}, orig_status{acc_nonexist};
bool is_special{false};
bool tick{false};
bool tock{false};
bool created{false};
bool split_depth_set_{false};
unsigned char split_depth_{0};
int verbosity{3 * 0};
ton::UnixTime now_{0};
ton::WorkchainId workchain{ton::workchainInvalid};
td::BitArray<32> addr_rewrite; // rewrite (anycast) data, split_depth bits
ton::StdSmcAddress addr; // rewritten address (by replacing a prefix of `addr_orig` with `addr_rewrite`)
ton::StdSmcAddress addr_orig; // address indicated in smart-contract data
Ref<vm::CellSlice> my_addr; // address as stored in the smart contract (MsgAddressInt)
Ref<vm::CellSlice> my_addr_exact; // exact address without anycast info
ton::LogicalTime last_trans_end_lt_;
ton::LogicalTime last_trans_lt_;
ton::Bits256 last_trans_hash_;
ton::LogicalTime block_lt;
ton::UnixTime last_paid;
vm::CellStorageStat storage_stat;
block::CurrencyCollection balance;
// td::RefInt256 balance;
// Ref<vm::Cell> extra_balance;
td::RefInt256 due_payment;
Ref<vm::Cell> orig_total_state; // ^Account
Ref<vm::Cell> total_state; // ^Account
Ref<vm::CellSlice> inner_state; // StateInit
ton::Bits256 state_hash; // hash of StateInit for frozen accounts
Ref<vm::Cell> code, data, library, orig_library;
std::vector<LtCellRef> transactions;
Account() = default;
Account(ton::WorkchainId wc, td::ConstBitPtr _addr) : workchain(wc), addr(_addr) {
}
Account(ton::WorkchainId wc, td::ConstBitPtr _addr, int depth)
: split_depth_set_(true), split_depth_((unsigned char)depth), workchain(wc), addr(_addr) {
}
block::CurrencyCollection get_balance() const {
return balance;
}
bool set_address(ton::WorkchainId wc, td::ConstBitPtr new_addr);
bool unpack(Ref<vm::CellSlice> account, Ref<vm::CellSlice> extra, ton::UnixTime now, bool special = false);
bool init_new(ton::UnixTime now);
bool recompute_tmp_addr(Ref<vm::CellSlice>& tmp_addr, int split_depth, td::ConstBitPtr orig_addr_rewrite) const;
td::RefInt256 compute_storage_fees(ton::UnixTime now, const std::vector<block::StoragePrices>& pricing) const;
bool is_masterchain() const {
return workchain == ton::masterchainId;
}
bool belongs_to_shard(ton::ShardIdFull shard) const;
bool store_acc_status(vm::CellBuilder& cb, int status) const;
bool store_acc_status(vm::CellBuilder& cb) const {
return store_acc_status(cb, status);
}
void push_transaction(Ref<vm::Cell> trans_root, ton::LogicalTime trans_lt);
bool libraries_changed() const;
bool create_account_block(vm::CellBuilder& cb); // stores an AccountBlock with all transactions
protected:
friend struct Transaction;
bool set_split_depth(int split_depth);
bool check_split_depth(int split_depth) const;
bool init_rewrite_addr(int split_depth, td::ConstBitPtr orig_addr_rewrite);
private:
bool unpack_address(vm::CellSlice& addr_cs);
bool unpack_storage_info(vm::CellSlice& cs);
bool unpack_state(vm::CellSlice& cs);
bool parse_maybe_anycast(vm::CellSlice& cs);
bool store_maybe_anycast(vm::CellBuilder& cb) const;
bool compute_my_addr(bool force = false);
};
struct Transaction {
static constexpr unsigned max_msg_bits = (1 << 21), max_msg_cells = (1 << 13);
enum {
tr_none,
tr_ord,
tr_storage,
tr_tick,
tr_tock,
tr_split_prepare,
tr_split_install,
tr_merge_prepare,
tr_merge_install
};
int trans_type{tr_none};
bool was_deleted{false};
bool was_frozen{false};
bool was_activated{false};
bool was_created{false};
bool bounce_enabled{false};
bool in_msg_extern{false};
bool use_msg_state{false};
bool is_first{false};
bool orig_addr_rewrite_set{false};
bool new_tick;
bool new_tock;
signed char new_split_depth{-1};
ton::UnixTime now;
int acc_status;
int verbosity{3 * 0};
int in_msg_type{0};
const Account& account; // only `commit` method modifies the account
Ref<vm::CellSlice> my_addr, my_addr_exact; // almost the same as in account.*
ton::LogicalTime start_lt, end_lt;
block::CurrencyCollection balance;
block::CurrencyCollection msg_balance_remaining;
td::RefInt256 due_payment;
td::RefInt256 in_fwd_fee, msg_fwd_fees;
block::CurrencyCollection total_fees{0};
ton::UnixTime last_paid;
Ref<vm::Cell> root;
Ref<vm::Cell> new_total_state;
Ref<vm::CellSlice> new_inner_state;
// Ref<vm::Cell> extra_balance;
// Ref<vm::Cell> msg_extra;
Ref<vm::Cell> new_code, new_data, new_library;
Ref<vm::Cell> in_msg, in_msg_state;
Ref<vm::CellSlice> in_msg_body;
Ref<vm::Cell> in_msg_library;
td::BitArray<256> frozen_hash;
td::BitArray<32> orig_addr_rewrite;
std::vector<Ref<vm::Cell>> out_msgs;
std::unique_ptr<StoragePhase> storage_phase;
std::unique_ptr<CreditPhase> credit_phase;
std::unique_ptr<ComputePhase> compute_phase;
std::unique_ptr<ActionPhase> action_phase;
std::unique_ptr<BouncePhase> bounce_phase;
vm::CellStorageStat new_storage_stat;
Transaction(const Account& _account, int ttype, ton::LogicalTime req_start_lt, ton::UnixTime _now,
Ref<vm::Cell> _inmsg = {});
bool unpack_input_msg(bool ihr_delivered, const ActionPhaseConfig* cfg);
bool check_in_msg_state_hash();
bool prepare_storage_phase(const StoragePhaseConfig& cfg, bool force_collect = true);
bool prepare_credit_phase();
bool compute_gas_limits(ComputePhase& cp, const ComputePhaseConfig& cfg);
Ref<vm::Stack> prepare_vm_stack(ComputePhase& cp);
std::vector<Ref<vm::Cell>> compute_vm_libraries(const ComputePhaseConfig& cfg);
bool prepare_compute_phase(const ComputePhaseConfig& cfg);
bool prepare_action_phase(const ActionPhaseConfig& cfg);
bool prepare_bounce_phase(const ActionPhaseConfig& cfg);
bool compute_state();
bool serialize();
td::uint64 gas_used() const {
return compute_phase ? compute_phase->gas_used : 0;
}
td::Result<vm::NewCellStorageStat::Stat> estimate_block_storage_profile_incr(
const vm::NewCellStorageStat& store_stat, const vm::CellUsageTree* usage_tree) const;
bool update_block_storage_profile(vm::NewCellStorageStat& store_stat, const vm::CellUsageTree* usage_tree) const;
bool would_fit(unsigned cls, const block::BlockLimitStatus& blk_lim_st) const;
bool update_limits(block::BlockLimitStatus& blk_lim_st) const;
Ref<vm::Cell> commit(Account& _account); // _account should point to the same account
LtCellRef extract_out_msg(unsigned i);
NewOutMsg extract_out_msg_ext(unsigned i);
void extract_out_msgs(std::vector<LtCellRef>& list);
private:
Ref<vm::Tuple> prepare_vm_c7(const ComputePhaseConfig& cfg) const;
bool prepare_rand_seed(td::BitArray<256>& rand_seed, const ComputePhaseConfig& cfg) const;
int try_action_set_code(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
int try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
int try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
bool check_replace_src_addr(Ref<vm::CellSlice>& src_addr) const;
bool check_rewrite_dest_addr(Ref<vm::CellSlice>& dest_addr, const ActionPhaseConfig& cfg,
bool* is_mc = nullptr) const;
bool serialize_storage_phase(vm::CellBuilder& cb);
bool serialize_credit_phase(vm::CellBuilder& cb);
bool serialize_compute_phase(vm::CellBuilder& cb);
bool serialize_action_phase(vm::CellBuilder& cb);
bool serialize_bounce_phase(vm::CellBuilder& cb);
bool unpack_msg_state(bool lib_only = false);
};
} // namespace block

200
crypto/common/AtomicRef.h Normal file
View file

@ -0,0 +1,200 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/SpinLock.h"
#include "common/refcnt.hpp"
#include <type_traits>
namespace td {
template <class T>
class AtomicRefSpinlock {
public:
AtomicRefSpinlock() = default;
AtomicRefSpinlock(Ref<T>&& ref) : ref_(ref.release()) {
}
~AtomicRefSpinlock() {
Ref<T>(ref_.load(std::memory_order_relaxed), typename Ref<T>::acquire_t{});
}
AtomicRefSpinlock(AtomicRefSpinlock&&) = delete;
AtomicRefSpinlock& operator=(AtomicRefSpinlock&&) = delete;
AtomicRefSpinlock(const AtomicRefSpinlock&) = delete;
AtomicRefSpinlock& operator=(const AtomicRefSpinlock&) = delete;
Ref<T> load() const {
auto guard = spin_lock_.lock();
return Ref<T>(ref_.load(std::memory_order_relaxed));
}
Ref<T> extract() const {
auto guard = spin_lock_.lock();
return Ref<T>(ref_.exchange(nullptr, std::memory_order_release), typename Ref<T>::acquire_t{});
}
Ref<T> load_unsafe() const {
return Ref<T>(get_unsafe());
}
const T* get_unsafe() const {
return ref_.load(std::memory_order_acquire);
}
bool store_if_empty(Ref<T>& desired) {
auto guard = spin_lock_.lock();
if (ref_.load(std::memory_order_relaxed) == nullptr) {
ref_.store(desired.release(), std::memory_order_release);
return true;
}
return false;
}
void store(Ref<T>&& ref) {
auto guard = spin_lock_.lock();
Ref<T>(ref_.exchange(ref.release(), std::memory_order_acq_rel), typename Ref<T>::acquire_t{});
}
private:
mutable SpinLock spin_lock_;
std::atomic<T*> ref_{nullptr};
};
template <class T>
class AtomicRefLockfree {
public:
AtomicRefLockfree() = default;
static constexpr int BATCH_SIZE = 100;
AtomicRefLockfree(Ref<T>&& ref) : ptr_(Ptr(ref.release(), BATCH_SIZE)) {
Ref<T>::acquire_shared(ptr_.load(std::memory_order_relaxed).ptr(), BATCH_SIZE);
}
~AtomicRefLockfree() {
auto ptr = ptr_.load(std::memory_order_relaxed);
if (ptr.ptr()) {
Ref<T>::release_shared(ptr.ptr(), ptr.ref_cnt() + 1);
}
}
AtomicRefLockfree(AtomicRefLockfree&&) = delete;
AtomicRefLockfree& operator=(AtomicRefLockfree&&) = delete;
AtomicRefLockfree(const AtomicRefLockfree&) = delete;
AtomicRefLockfree& operator=(const AtomicRefLockfree&) = delete;
Ref<T> load() const {
auto ptr = ptr_.load();
while (ptr.ptr()) {
if (ptr.ref_cnt() == 0) {
td::this_thread::yield();
ptr = ptr_.load();
continue;
}
auto new_ptr = Ptr(ptr.ptr(), ptr.ref_cnt() - 1);
if (ptr_.compare_exchange_weak(ptr, new_ptr)) {
if (new_ptr.ref_cnt() < BATCH_SIZE / 2) {
try_reserve(ptr.ptr());
}
return Ref<T>(ptr.ptr(), typename Ref<T>::acquire_t{});
}
}
return {};
}
void try_reserve(T* raw_ptr) const {
int reserve_cnt = BATCH_SIZE;
Ref<T>::acquire_shared(raw_ptr, reserve_cnt);
auto ptr = ptr_.load();
while (ptr.ptr() == raw_ptr && ptr.ref_cnt() < BATCH_SIZE / 2) {
auto new_ptr = Ptr(ptr.ptr(), ptr.ref_cnt() + reserve_cnt);
if (ptr_.compare_exchange_weak(ptr, new_ptr)) {
return;
}
}
Ref<T>::release_shared(raw_ptr, reserve_cnt);
}
Ref<T> extract() {
auto ptr = ptr_.exchange({});
if (ptr.ref_cnt() != 0) {
Ref<T>::release_shared(ptr.ptr(), ptr.ref_cnt());
}
return Ref<T>(ptr.ptr(), typename Ref<T>::acquire_t{});
}
Ref<T> load_unsafe() const {
return load();
}
T* get_unsafe() const {
return ptr_.load().ptr();
}
bool store_if_empty(Ref<T>& desired) {
auto raw_ptr = desired.get();
Ref<T>::acquire_shared(raw_ptr, BATCH_SIZE + 1);
Ptr new_ptr{const_cast<T*>(raw_ptr), BATCH_SIZE};
auto ptr = ptr_.load();
while (ptr.ptr() == nullptr) {
if (ptr_.compare_exchange_weak(ptr, new_ptr)) {
return true;
}
}
Ref<T>::release_shared(raw_ptr, BATCH_SIZE + 1);
return false;
}
void store(Ref<T>&& ref) {
Ptr new_ptr = [&]() -> Ptr {
if (ref.is_null()) {
return {};
}
auto raw_ptr = ref.release();
Ref<T>::acquire_shared(raw_ptr, BATCH_SIZE);
return {raw_ptr, BATCH_SIZE};
}();
auto ptr = ptr_.load();
while (!ptr_.compare_exchange_weak(ptr, new_ptr)) {
}
if (ptr.ptr()) {
Ref<T>::release_shared(ptr.ptr(), ptr.ref_cnt() + 1);
}
}
private:
struct Ptr {
public:
Ptr() = default;
Ptr(T* ptr, int ref_cnt) {
data_ = reinterpret_cast<td::uint64>(ptr);
CHECK((data_ >> 48) == 0);
data_ |= static_cast<td::uint64>(ref_cnt) << 48;
}
T* ptr() const {
return reinterpret_cast<T*>(data_ & (std::numeric_limits<uint64>::max() >> 16));
}
int ref_cnt() const {
return static_cast<int>(data_ >> 48);
}
private:
td::uint64 data_{0};
};
static_assert(sizeof(Ptr) == 8, "sizeof(Ptr) must be 8 for atomic to work fine");
static_assert(std::is_trivially_copyable<Ptr>::value, "Ptr must be tribially copyable");
mutable std::atomic<Ptr> ptr_{Ptr()};
};
template <class T>
using AtomicRef = AtomicRefLockfree<T>;
} // namespace td

41
crypto/common/bigint.cpp Normal file
View file

@ -0,0 +1,41 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "common/bigint.hpp"
namespace td {
template class AnyIntView<BigIntInfo>;
template class BigIntG<257, BigIntInfo>;
namespace literals {
BigInt256 operator""_i256(const char* str, std::size_t str_len) {
BigInt256 x;
x.enforce(x.parse_dec(str, (int)str_len) == (int)str_len);
return x;
}
BigInt256 operator""_x256(const char* str, std::size_t str_len) {
BigInt256 x;
x.enforce(x.parse_hex(str, (int)str_len) == (int)str_len);
return x;
}
} // namespace literals
} // namespace td

2525
crypto/common/bigint.hpp Normal file

File diff suppressed because it is too large Load diff

668
crypto/common/bitstring.cpp Normal file
View file

@ -0,0 +1,668 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "common/bitstring.h"
#include <cstring>
#include <limits>
#include "td/utils/as.h"
#include "td/utils/bits.h"
#include "td/utils/misc.h"
#include "crypto/openssl/digest.h"
namespace td {
template class Ref<BitString>;
BitString::BitString(const BitSlice& bs, unsigned reserve_bits) {
if (!bs.size() && !reserve_bits) {
ptr = 0;
offs = len = bytes_alloc = 0;
} else {
offs = bs.get_offs();
len = bs.size();
bytes_alloc = (bs.get_offs() + bs.size() + reserve_bits + 7) >> 3;
ptr = static_cast<unsigned char*>(std::malloc(bytes_alloc));
CHECK(ptr);
if (bs.size()) {
std::memcpy(ptr, bs.get_ptr(), bs.byte_size());
}
}
}
BitString::BitString(unsigned reserve_bits) {
if (!reserve_bits) {
ptr = 0;
offs = len = bytes_alloc = 0;
} else {
bytes_alloc = (reserve_bits + 7) >> 3;
ptr = static_cast<unsigned char*>(std::malloc(bytes_alloc));
CHECK(ptr);
offs = len = 0;
}
}
BitString::operator BitSlice() const {
return BitSlice(BitStringRef{this}, ptr, offs, len);
}
BitString* BitString::make_copy() const {
if (!ptr) {
return new BitString(64); // reserve 64 bits
} else {
return new BitString(operator BitSlice(), 64);
}
}
BitString& BitString::reserve_bits(unsigned req_bits) {
req_bits += offs + len;
if (req_bits > bytes_alloc * 8) {
bytes_alloc = (req_bits + 7) >> 3;
ptr = (unsigned char*)std::realloc(ptr, bytes_alloc);
CHECK(ptr);
}
return *this;
}
BitSliceWrite BitString::reserve_bitslice(unsigned req_bits) {
reserve_bits(req_bits);
unsigned pos = offs + len;
len += req_bits;
return BitSliceWrite(Ref<BitString>(this), ptr, pos, req_bits);
}
BitString& BitString::append(const BitSlice& bs) {
reserve_bitslice(bs.size()) = bs;
return *this;
}
BitSlice BitString::subslice(unsigned from, unsigned bits) const {
return BitSlice{BitStringRef{this}, ptr, static_cast<int>(offs + from), bits};
}
BitSliceWrite BitString::subslice_write(unsigned from, unsigned bits) {
return BitSliceWrite{BitStringRef{this}, ptr, offs + from, bits};
}
const BitSliceWrite& BitSliceWrite::operator=(const BitSlice& bs) const {
if (size() != bs.size()) {
throw LengthMismatch();
}
bitstring::bits_memcpy(get_ptr(), get_offs(), bs.get_ptr(), bs.get_offs(), size());
return *this;
}
const BitSliceWrite& BitSliceWrite::operator=(bool val) const {
bitstring::bits_memset(get_ptr(), get_offs(), val, size());
return *this;
}
std::ostream& operator<<(std::ostream& os, const BitString& bs) {
return os << bs.to_hex();
}
std::ostream& operator<<(std::ostream& os, Ref<BitString> bs_ref) {
return os << (bs_ref.is_null() ? "(null-bs)" : bs_ref->to_hex());
}
namespace bitstring {
void bits_memcpy(unsigned char* to, int to_offs, const unsigned char* from, int from_offs, std::size_t bit_count) {
if (bit_count <= 0) {
return;
}
from += (from_offs >> 3);
to += (to_offs >> 3);
from_offs &= 7;
to_offs &= 7;
//fprintf(stderr, "bits_memcpy: from=%p (%02x) to=%p (%02x) from_offs=%d to_offs=%d count=%lu\n", from, *from, to, *to, from_offs, to_offs, bit_count);
int sz = (int)bit_count;
bit_count += from_offs;
if (from_offs == to_offs) {
if (bit_count < 8) {
int mask = (-0x100 >> bit_count) & (0xff >> to_offs);
*to = (unsigned char)((*to & ~mask) | (*from & mask));
return;
}
std::size_t l = (bit_count >> 3);
if (!to_offs) {
std::memcpy(to, from, l);
} else {
int mask = (0xff >> to_offs);
*to = (unsigned char)((*to & ~mask) | (*from & mask));
std::memcpy(to + 1, from + 1, l - 1);
}
if ((bit_count &= 7) != 0) {
int mask = (-0x100 >> bit_count);
to[l] = (unsigned char)((to[l] & ~mask) | (from[l] & mask));
}
} else {
int b = (int)to_offs;
unsigned long long acc = (b ? *to >> (8 - b) : 0);
if (bit_count < 8) {
acc <<= sz;
acc |= ((*from & (0xff >> from_offs)) >> (8 - bit_count));
b += sz;
} else {
unsigned ld = 8 - from_offs;
acc <<= ld;
acc |= (*from++ & (0xff >> from_offs));
b += ld;
bit_count -= 8;
// b <= 15 here
while (bit_count >= 32) {
acc <<= 32;
acc |= td::bswap32(as<unsigned>(from));
from += 4;
as<unsigned>(to) = td::bswap32((unsigned)(acc >> b));
to += 4;
bit_count -= 32;
}
// bit_count <= 31, b <= 15
while (bit_count >= 8) {
acc <<= 8;
acc |= *from++;
bit_count -= 8;
b += 8;
}
// b + bit_count = const <= 46
if (bit_count > 0) {
acc <<= bit_count;
acc |= (*from >> (8 - bit_count));
b += (int)bit_count;
}
}
while (b >= 8) {
b -= 8;
*to++ = (unsigned char)(acc >> b);
}
if (b > 0) {
*to = (unsigned char)((*to & (0xff >> b)) | ((int)acc << (8 - b)));
}
}
}
void bits_memcpy(BitPtr to, ConstBitPtr from, std::size_t bit_count) {
bits_memcpy(to.ptr, to.offs, from.ptr, from.offs, bit_count);
}
void bits_memset(unsigned char* to, int to_offs, bool val, std::size_t bit_count) {
if (bit_count <= 0) {
return;
}
to += (to_offs >> 3);
to_offs &= 7;
int sz = (int)bit_count;
bit_count += to_offs;
int c = *to;
if (bit_count <= 8) {
int mask = (((-0x100 >> sz) & 0xff) >> to_offs);
if (val) {
*to = (unsigned char)(c | mask);
} else {
*to = (unsigned char)(c & ~mask);
}
return;
}
if (val) {
*to = (unsigned char)(c | (0xff >> to_offs));
} else {
*to = (unsigned char)(c & (-0x100 >> to_offs));
}
std::size_t l = (bit_count >> 3);
std::memset(to + 1, val ? 0xff : 0, l - 1);
if ((bit_count &= 7) != 0) {
if (val) {
to[l] = (unsigned char)(to[l] | (-0x100 >> bit_count));
} else {
to[l] = (unsigned char)(to[l] & (0xff >> bit_count));
}
}
}
void bits_memset(BitPtr to, bool val, std::size_t bit_count) {
bits_memset(to.ptr, to.offs, val, bit_count);
}
std::size_t bits_memscan_rev(const unsigned char* ptr, int offs, std::size_t bit_count, bool cmp_to) {
if (!bit_count) {
return 0;
}
int xor_val = (cmp_to ? -1 : 0);
ptr += ((offs + bit_count) >> 3);
offs = (int)((offs + bit_count) & 7);
std::size_t res = offs;
if (offs) {
unsigned v = (*ptr >> (8 - offs)) ^ xor_val;
unsigned c = td::count_trailing_zeroes32(v);
if (c < (unsigned)offs || res >= bit_count) {
return std::min(c, (unsigned)bit_count);
}
}
bit_count -= res;
while (bit_count >= 32) {
ptr -= 4;
unsigned v = td::bswap32(as<unsigned>(ptr)) ^ xor_val;
if (v) {
return td::count_trailing_zeroes_non_zero32(v) + res;
}
res += 32;
bit_count -= 32;
}
xor_val &= 0xff;
while (bit_count >= 8) {
unsigned v = *--ptr ^ xor_val;
if (v) {
return td::count_trailing_zeroes_non_zero32(v) + res;
}
res += 8;
bit_count -= 8;
}
if (bit_count > 0) {
unsigned v = *--ptr ^ xor_val;
return std::min((unsigned)td::count_trailing_zeroes32(v), (unsigned)bit_count) + res;
} else {
return res;
}
}
std::size_t bits_memscan(const unsigned char* ptr, int offs, std::size_t bit_count, bool cmp_to) {
if (!bit_count) {
return 0;
}
int xor_val = -static_cast<int>(cmp_to);
ptr += (offs >> 3);
offs &= 7;
std::size_t rem = bit_count;
unsigned v, c;
if (offs) {
v = ((unsigned)(ptr[0] ^ xor_val) << (24 + offs));
// std::cerr << "[A] rem=" << rem << " ptr=" << (const void*)ptr << " v=" << std::hex << v << std::dec << std::endl;
c = td::count_leading_zeroes32(v);
unsigned l = (unsigned)(8 - offs);
if (c < l || bit_count <= l) {
return std::min<std::size_t>(c, bit_count);
}
rem -= l;
ptr++;
}
while (rem >= 8 && !td::is_aligned_pointer<8>(ptr)) {
v = ((*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);
}
rem -= 8;
}
td::uint64 xor_val_l = (cmp_to ? ~0LL : 0LL);
while (rem >= 64) {
td::uint64 z = td::bswap64(as<td::uint64>(ptr)) ^ xor_val_l;
// std::cerr << "[C] rem=" << rem << " ptr=" << (const void*)ptr << " z=" << std::hex << z << std::dec << std::endl;
if (z) {
return bit_count - rem + td::count_leading_zeroes_non_zero64(z);
}
ptr += 8;
rem -= 64;
}
while (rem >= 8) {
v = ((*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);
}
rem -= 8;
}
if (rem > 0) {
v = ((*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;
} else {
return bit_count;
}
}
std::size_t bits_memscan(ConstBitPtr bs, std::size_t bit_count, bool cmp_to) {
return bits_memscan(bs.ptr, bs.offs, bit_count, cmp_to);
}
std::size_t bits_memscan_rev(ConstBitPtr bs, std::size_t bit_count, bool cmp_to) {
return bits_memscan_rev(bs.ptr, bs.offs, bit_count, cmp_to);
}
int bits_memcmp(const unsigned char* bs1, int bs1_offs, const unsigned char* bs2, int bs2_offs, std::size_t bit_count,
std::size_t* same_upto) {
if (!bit_count) {
return 0;
}
bs1 += (bs1_offs >> 3);
bs2 += (bs2_offs >> 3);
bs1_offs &= 7;
bs2_offs &= 7;
//fprintf(stderr, "bits_memcmp: bs1=%02x%02x offs=%d bs2=%02x%02x offs=%d cnt=%lu\n", bs1[0], bs1[1], bs1_offs, bs2[0], bs2[1], bs2_offs, bit_count);
unsigned long long acc1 = (((unsigned long long)*bs1++) << (56 + bs1_offs));
int z1 = 8 - bs1_offs;
unsigned long long acc2 = (((unsigned long long)*bs2++) << (56 + bs2_offs));
int z2 = 8 - bs2_offs;
std::size_t processed = 0;
while (bit_count >= 40) {
acc1 |= ((unsigned long long)td::bswap32(as<unsigned>(bs1)) << (32 - z1));
bs1 += 4;
acc2 |= ((unsigned long long)td::bswap32(as<unsigned>(bs2)) << (32 - z2));
bs2 += 4;
if ((acc1 ^ acc2) & (~0ULL << 32)) {
if (same_upto) {
*same_upto = processed + td::count_leading_zeroes64(acc1 ^ acc2);
}
return acc1 < acc2 ? -1 : 1;
}
acc1 <<= 32;
acc2 <<= 32;
processed += 32;
bit_count -= 32;
}
// now 0 <= bit_count <= 39
bs1_offs += (int)bit_count - 8; // = bit_count - z1, bits to load from bs1
while (bs1_offs >= 8) {
acc1 |= ((unsigned long long)(*bs1++) << (56 - z1));
z1 += 8;
bs1_offs -= 8;
}
if (bs1_offs > 0) {
acc1 |= ((unsigned long long)(*bs1) << (56 - z1));
}
z1 += bs1_offs; // NB: bs1_offs may be negative
bs2_offs += (int)bit_count - 8; // bits to load from bs2
while (bs2_offs >= 8) {
acc2 |= ((unsigned long long)(*bs2++) << (56 - z2));
z2 += 8;
bs2_offs -= 8;
}
if (bs2_offs > 0) {
acc2 |= ((unsigned long long)(*bs2) << (56 - z2));
}
z2 += bs2_offs;
CHECK(z1 == z2);
CHECK(z1 < 64);
//fprintf(stderr, "acc1=%016llx acc2=%016llx z1=z2=%d\n", acc1, acc2, z1);
if (z1) {
if ((acc1 ^ acc2) & (~0ULL << (64 - z1))) {
if (same_upto) {
*same_upto = processed + td::count_leading_zeroes64(acc1 ^ acc2);
}
return acc1 < acc2 ? -1 : 1;
}
}
if (same_upto) {
*same_upto = processed + bit_count;
}
return 0;
}
int bits_memcmp(ConstBitPtr bs1, ConstBitPtr bs2, std::size_t bit_count, std::size_t* same_upto) {
return bits_memcmp(bs1.ptr, bs1.offs, bs2.ptr, bs2.offs, bit_count, same_upto);
}
int bits_lexcmp(const unsigned char* bs1, int bs1_offs, std::size_t bs1_bit_count, const unsigned char* bs2,
int bs2_offs, std::size_t bs2_bit_count) {
int res = bits_memcmp(bs1, bs1_offs, bs2, bs2_offs, std::min(bs1_bit_count, bs2_bit_count), 0);
if (res || bs1_bit_count == bs2_bit_count) {
return res;
}
return bs1_bit_count < bs2_bit_count ? -1 : 1;
}
int bits_lexcmp(ConstBitPtr bs1, std::size_t bs1_bit_count, ConstBitPtr bs2, std::size_t bs2_bit_count) {
return bits_lexcmp(bs1.ptr, bs1.offs, bs1_bit_count, bs2.ptr, bs2.offs, bs2_bit_count);
}
void bits_store_long_top(unsigned char* to, int to_offs, unsigned long long val, unsigned top_bits) {
CHECK(top_bits <= 64);
if (top_bits <= 0) {
return;
}
to += (to_offs >> 3);
to_offs &= 7;
if (!to_offs && !(top_bits & 7)) {
// good only on little-endian machines!
unsigned long long tmp = td::bswap64(val);
std::memcpy(to, &tmp, top_bits >> 3);
return;
}
unsigned long long z = (unsigned long long)(*to & (-0x100 >> to_offs)) << 56;
z |= (val >> to_offs);
top_bits += to_offs;
if (top_bits > 64) {
as<unsigned long long>(to) = td::bswap64(z);
z = (val << (8 - to_offs));
int mask = (0xff >> (top_bits - 64));
to[8] = (unsigned char)((to[8] & mask) | ((int)z & ~mask));
} else {
int p = 56, q = 64 - top_bits;
if (q <= 32) {
as<unsigned>(to) = td::bswap32((unsigned)(z >> 32));
to += 4;
p -= 32;
}
while (p >= q) {
*to++ = (unsigned char)(z >> p);
p -= 8;
}
top_bits = p + 8 - q;
if (top_bits > 0) {
int mask = (0xff >> top_bits);
*to = (unsigned char)((*to & mask) | ((z >> p) & ~mask));
}
}
}
void bits_store_long_top(BitPtr to, unsigned long long val, unsigned top_bits) {
bits_store_long_top(to.ptr, to.offs, val, top_bits);
}
void bits_store_long(BitPtr to, unsigned long long val, unsigned bits) {
bits_store_long_top(to, val << (64 - bits), bits);
}
unsigned long long bits_load_long_top(const unsigned char* from, int from_offs, unsigned top_bits) {
CHECK(top_bits <= 64);
if (!top_bits) {
return 0;
}
from += (from_offs >> 3);
from_offs &= 7;
if ((unsigned)from_offs + top_bits <= 64) {
unsigned long long tmp;
std::memcpy(&tmp, from, (from_offs + top_bits + 7) >> 3);
return (td::bswap64(tmp) << from_offs) & (std::numeric_limits<td::uint64>::max() << (64 - top_bits));
} else {
unsigned long long z = td::bswap64(as<unsigned long long>(from));
z <<= from_offs;
z |= (from[8] >> (8 - from_offs));
return z & (std::numeric_limits<td::uint64>::max() << (64 - top_bits));
}
}
unsigned long long bits_load_long_top(ConstBitPtr from, unsigned top_bits) {
return bits_load_long_top(from.ptr, from.offs, top_bits);
}
unsigned long long bits_load_ulong(ConstBitPtr from, unsigned bits) {
return bits_load_long_top(from, bits) >> (64 - bits);
}
long long bits_load_long(ConstBitPtr from, unsigned bits) {
return (long long)bits_load_long_top(from, bits) >> (64 - bits);
}
std::string bits_to_binary(const unsigned char* ptr, int offs, std::size_t len) {
if (!len) {
return "";
}
std::string s;
s.reserve(len);
ptr += (offs >> 3);
unsigned mask = (0x80 >> (offs & 7));
unsigned value = *ptr++;
do {
s.push_back(value & mask ? '1' : '0');
if (!(mask >>= 1)) {
value = *ptr++;
mask = 0x80;
}
} while (--len > 0);
return s;
}
std::string bits_to_binary(ConstBitPtr bs, std::size_t len) {
return bits_to_binary(bs.ptr, bs.offs, len);
}
static const char hex_digits[] = "0123456789ABCDEF";
std::string bits_to_hex(const unsigned char* ptr, int offs, std::size_t len) {
if (!len) {
return "";
}
std::string s;
s.reserve((len + 7) >> 2);
ptr += (offs >> 3);
offs &= 7;
unsigned long long acc = *ptr++ & (0xff >> offs);
unsigned bits = 8 - offs;
if (bits > len) {
acc >>= bits - (unsigned)len;
bits = (unsigned)len;
} else {
len -= bits;
while (len >= 8) {
while (len >= 8 && bits <= 56) {
acc <<= 8;
acc |= *ptr++;
bits += 8;
len -= 8;
}
while (bits >= 4) {
bits -= 4;
s.push_back(hex_digits[(acc >> bits) & 15]);
}
}
if (len > 0) {
acc <<= len;
acc |= (*ptr >> (8 - len));
bits += (unsigned)len;
}
}
int f = bits & 3;
if (f) {
acc = (2 * acc + 1) << (3 - f);
bits += 4 - f;
}
while (bits >= 4) {
bits -= 4;
s.push_back(hex_digits[(acc >> bits) & 15]);
}
CHECK(!bits);
if (f) {
s.push_back('_');
}
return s;
}
std::string bits_to_hex(ConstBitPtr bs, std::size_t len) {
return bits_to_hex(bs.ptr, bs.offs, len);
}
long parse_bitstring_hex_literal(unsigned char* buff, std::size_t buff_size, const char* str, const char* str_end) {
std::size_t hex_digits_count = 0;
bool cmpl = false;
unsigned char* ptr = buff;
const char* rptr = str;
while (rptr < str_end) {
int c = *rptr++;
if (c == ' ' || c == '\t') {
continue;
}
if (cmpl) {
return td::narrow_cast<long>(str - rptr);
}
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
int val = (c <= '9') ? c - '0' : ((c | 0x20) - 'a' + 10);
if (hex_digits_count >= 2 * buff_size) {
return td::narrow_cast<long>(str - rptr);
}
if (!(hex_digits_count & 1)) {
*ptr = (unsigned char)(val << 4);
} else {
*ptr = (unsigned char)(*ptr | val);
ptr++;
}
hex_digits_count++;
continue;
}
if (c == '_') {
cmpl = true;
} else {
return td::narrow_cast<long>(str - rptr);
}
}
std::size_t bits = 4 * hex_digits_count;
if (cmpl && bits) {
int t = (hex_digits_count & 1) ? (0x100 + *ptr) >> 4 : (0x100 + *--ptr);
while (bits > 0) {
--bits;
if (t & 1) {
break;
}
t >>= 1;
if (t == 1) {
t = 0x100 + *--ptr;
}
}
}
return bits;
}
long parse_bitstring_binary_literal(BitPtr buff, std::size_t buff_size, const char* str, const char* str_end) {
const char* ptr = str;
while (ptr < str_end && buff_size && (*ptr == '0' || *ptr == '1')) {
*buff++ = (bool)(*ptr++ & 1);
--buff_size;
}
return td::narrow_cast<long>(ptr == str_end ? ptr - str : str - ptr - 1);
}
void bits_sha256(BitPtr to, ConstBitPtr from, std::size_t size) {
if (from.byte_aligned() && !(size & 7)) {
if (to.byte_aligned()) {
digest::hash_str<digest::SHA256>(to.get_byte_ptr(), from.get_byte_ptr(), size >> 3);
} else {
unsigned char buffer[32];
digest::hash_str<digest::SHA256>(buffer, from.get_byte_ptr(), size >> 3);
to.copy_from(BitPtr{buffer}, 256);
}
} else {
throw BitstringError{};
}
}
} // namespace bitstring
} // namespace td

662
crypto/common/bitstring.h Normal file
View file

@ -0,0 +1,662 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "common/refcnt.hpp"
#include <utility>
#include <array>
#include <string>
#include <ostream>
#include <cstdlib>
#include "td/utils/bits.h"
namespace td {
template <class Pt>
struct BitPtrGen;
typedef BitPtrGen<unsigned char> BitPtr;
typedef BitPtrGen<const unsigned char> ConstBitPtr;
struct BitstringError {};
namespace bitstring {
void bits_memcpy(unsigned char* to, int to_offs, const unsigned char* from, int from_offs, std::size_t bit_count);
void bits_memcpy(BitPtr to, ConstBitPtr from, std::size_t bit_count);
void bits_memset(unsigned char* to, int to_offs, bool val, std::size_t bit_count);
void bits_memset(BitPtr to, bool val, std::size_t bit_count);
int bits_memcmp(const unsigned char* bs1, int bs1_offs, const unsigned char* bs2, int bs2_offs, std::size_t bit_count,
std::size_t* same_upto = 0);
int bits_memcmp(ConstBitPtr bs1, ConstBitPtr bs2, std::size_t bit_count, std::size_t* same_upto = 0);
int bits_lexcmp(const unsigned char* bs1, int bs1_offs, std::size_t bs1_bit_count, const unsigned char* bs2,
int bs2_offs, std::size_t bs2_bit_count);
int bits_lexcmp(ConstBitPtr bs1, std::size_t bs1_bit_count, ConstBitPtr bs2, std::size_t bs2_bit_count);
std::size_t bits_memscan(const unsigned char* ptr, int offs, std::size_t bit_count, bool cmp_to);
std::size_t bits_memscan_rev(const unsigned char* ptr, int offs, std::size_t bit_count, bool cmp_to);
std::size_t bits_memscan(ConstBitPtr bs, std::size_t bit_count, bool cmp_to);
std::size_t bits_memscan_rev(ConstBitPtr bs, std::size_t bit_count, bool cmp_to);
void bits_store_long_top(unsigned char* to, int to_offs, unsigned long long val, unsigned top_bits);
void bits_store_long_top(BitPtr to, unsigned long long val, unsigned top_bits);
void bits_store_long(BitPtr to, unsigned long long val, unsigned bits);
unsigned long long bits_load_long_top(const unsigned char* from, int from_offs, unsigned top_bits);
unsigned long long bits_load_long_top(ConstBitPtr from, unsigned top_bits);
long long bits_load_long(ConstBitPtr from, unsigned bits);
unsigned long long bits_load_ulong(ConstBitPtr from, unsigned bits);
long parse_bitstring_hex_literal(unsigned char* buff, std::size_t buff_size, const char* str, const char* str_end);
long parse_bitstring_binary_literal(BitPtr buff, std::size_t buff_size, const char* str, const char* str_end);
void bits_sha256(BitPtr to, ConstBitPtr from, std::size_t size);
std::string bits_to_binary(const unsigned char* ptr, int offs, std::size_t len);
std::string bits_to_binary(ConstBitPtr bs, std::size_t len);
std::string bits_to_hex(const unsigned char* ptr, int offs, std::size_t len);
std::string bits_to_hex(ConstBitPtr bs, std::size_t len);
} // namespace bitstring
template <class Pt>
struct BitPtrGen {
Pt* ptr;
int offs;
BitPtrGen(Pt* _ptr, int _offs = 0) : ptr(_ptr), offs(_offs) {
}
template <class Pt2>
BitPtrGen(BitPtrGen<Pt2> val) : ptr(val.ptr), offs(val.offs) {
}
BitPtrGen& operator+=(int _offs) {
offs += _offs;
return *this;
}
BitPtrGen& operator-=(int _offs) {
offs -= _offs;
return *this;
}
void advance(int _offs) {
offs += _offs;
}
bool byte_aligned() const {
return !(offs & 7);
}
Pt* get_byte_ptr() const {
return ptr + (offs >> 3);
}
void copy_from(BitPtrGen<const Pt> from, unsigned size) const {
bitstring::bits_memcpy(*this, from, size);
}
BitPtrGen& concat(BitPtrGen<const Pt> from, unsigned size) {
bitstring::bits_memcpy(*this, from, size);
offs += size;
return *this;
}
template <typename T>
void copy_from(const T& from) const {
copy_from(from.bits(), from.size());
}
template <typename T>
BitPtrGen& concat(const T& from) {
return concat(from.bits(), from.size());
}
void fill(bool bit, unsigned size) {
bitstring::bits_memset(*this, bit, size);
}
BitPtrGen& concat_same(bool bit, unsigned size) {
bitstring::bits_memset(*this, bit, size);
offs += size;
return *this;
}
int compare(BitPtrGen<const Pt> other, std::size_t size, std::size_t* same_upto = nullptr) const {
return bitstring::bits_memcmp(*this, other, size, same_upto);
}
bool equals(BitPtrGen<const Pt> other, std::size_t size) const {
return !bitstring::bits_memcmp(*this, other, size);
}
std::size_t scan(bool value, std::size_t len) const {
return bitstring::bits_memscan(*this, len, value);
}
long long get_int(unsigned bits) const {
return bitstring::bits_load_long(*this, bits);
}
unsigned long long get_uint(unsigned bits) const {
return bitstring::bits_load_ulong(*this, bits);
}
void store_uint(unsigned long long val, unsigned n) const {
bitstring::bits_store_long(*this, val, n);
}
void store_int(long long val, unsigned n) const {
bitstring::bits_store_long(*this, val, n);
}
BitPtrGen operator+(int _offs) const {
return BitPtrGen{ptr, offs + _offs};
}
BitPtrGen operator-(int _offs) const {
return BitPtrGen{ptr, offs - _offs};
}
BitPtrGen operator++() {
++offs;
return *this;
}
BitPtrGen operator--() {
--offs;
return *this;
}
BitPtrGen operator++(int) {
return BitPtrGen{ptr, offs++};
}
BitPtrGen operator--(int) {
return BitPtrGen{ptr, offs--};
}
bool is_null() const {
return !ptr;
}
bool not_null() const {
return ptr;
}
class BitSelector {
Pt* ptr;
unsigned char mask;
public:
BitSelector(Pt* _ptr, int offs) {
ptr = _ptr + (offs >> 3);
mask = (unsigned char)(0x80 >> (offs & 7));
}
bool clear() {
*ptr = (unsigned char)(*ptr & ~mask);
return false;
}
bool set() {
*ptr = (unsigned char)(*ptr | mask);
return true;
}
bool operator=(bool val) {
return val ? set() : clear();
}
operator bool() const {
return *ptr & mask;
}
};
BitSelector operator*() const {
return BitSelector{ptr, offs};
}
BitSelector operator[](int i) const {
return BitSelector{ptr, offs + i};
}
std::string to_hex(std::size_t len) const {
return bitstring::bits_to_hex(*this, len);
}
std::string to_binary(std::size_t len) const {
return bitstring::bits_to_binary(*this, len);
}
};
template <class Rf, class Pt>
class BitSliceGen {
Rf ref;
Pt* ptr;
unsigned offs, len;
public:
struct BitSliceError {};
BitSliceGen() : ref(), ptr(0), offs(0), len(0) {
}
BitSliceGen(Rf _ref, Pt* _ptr, int _offs, unsigned _len)
: ref(std::move(_ref)), ptr(_ptr + (_offs >> 3)), offs(_offs & 7), len(_len) {
}
BitSliceGen(const BitSliceGen& bs, unsigned _offs, unsigned _len);
BitSliceGen(BitSliceGen&& bs, unsigned _offs, unsigned _len);
BitSliceGen(Pt* _ptr, unsigned _len) : ref(), ptr(_ptr), offs(0), len(_len) {
}
~BitSliceGen() {
}
Pt* get_ptr() const {
return ptr;
}
BitPtrGen<Pt> bits() const {
return BitPtrGen<Pt>{ptr, (int)offs};
}
bool is_valid() const {
return ptr != 0;
}
unsigned char cur_byte() const {
return *ptr;
}
unsigned get_offs() const {
return offs;
}
unsigned size() const {
return len;
}
unsigned byte_size() const {
return (offs + len + 7) >> 3;
}
void ensure_throw(bool cond) const {
if (!cond) {
throw BitSliceError{};
}
}
BitSliceGen& assign(Rf _ref, Pt* _ptr, unsigned _offs, unsigned _len);
void forget() {
ref.clear();
ptr = 0;
offs = len = 0;
}
bool operator[](unsigned i) const {
i += offs;
return ptr[i >> 3] & (0x80 >> (i & 7));
}
bool at(unsigned i) const {
ensure_throw(i < len);
return operator[](i);
}
bool advance_bool(unsigned bits);
bool set_size_bool(unsigned bits) {
if (bits > len) {
return false;
}
len = bits;
return true;
}
BitSliceGen& advance(unsigned bits) {
ensure_throw(advance_bool(bits));
return *this;
}
BitSliceGen& set_size(unsigned bits) {
ensure_throw(set_size_bool(bits));
return *this;
}
BitSliceGen subslice(unsigned from, unsigned bits) const & {
return BitSliceGen(*this, from, bits);
}
BitSliceGen subslice(unsigned from, unsigned bits) && {
return BitSliceGen(*this, from, bits);
}
void copy_to(BitPtr to) const {
bitstring::bits_memcpy(to, bits(), size());
}
int compare(ConstBitPtr other) const {
return bitstring::bits_memcmp(bits(), other, size());
}
bool operator==(ConstBitPtr other) const {
return !compare(other);
}
bool operator!=(ConstBitPtr other) const {
return compare(other);
}
std::string to_binary() const {
return bitstring::bits_to_binary(ptr, offs, len);
}
std::string to_hex() const {
return bitstring::bits_to_hex(ptr, offs, len);
}
void dump(std::ostream& stream, bool nocr = false) const {
stream << "[" << offs << "," << len << "]";
if (!nocr) {
stream << std::endl;
}
}
protected:
inline Pt& cur_byte_w() const {
return *ptr;
}
};
template <class Rf, class Pt>
BitSliceGen<Rf, Pt>::BitSliceGen(const BitSliceGen<Rf, Pt>& bs, unsigned from, unsigned bits) : ref() {
if (from >= bs.size() || bits > bs.size() - from) {
ptr = 0;
offs = len = 0;
return;
}
//bs.dump(std::cout, true);
//std::cout << ".subslice(" << from << "," << bits << ") = ";
ref = bs.ref;
offs = bs.offs + from;
ptr = bs.ptr + (offs >> 3);
offs &= 7;
len = bits;
//dump(std::cout);
}
template <class Rf, class Pt>
BitSliceGen<Rf, Pt>::BitSliceGen(BitSliceGen&& bs, unsigned from, unsigned bits) : ref() {
if (from >= bs.size() || bits > bs.size() - from) {
ptr = 0;
offs = len = 0;
return;
}
ref = std::move(bs.ref);
offs = bs.offs + from;
ptr = bs.ptr + (offs >> 3);
offs &= 7;
len = bits;
}
template <class Rf, class Pt>
BitSliceGen<Rf, Pt>& BitSliceGen<Rf, Pt>::assign(Rf _ref, Pt* _ptr, unsigned _offs, unsigned _len) {
ref = std::move(_ref);
ptr = _ptr + (_offs >> 3);
offs = (_offs & 7);
len = _len;
return *this;
}
template <class Rf, class Pt>
inline bool BitSliceGen<Rf, Pt>::advance_bool(unsigned bits) {
if (len < bits) {
return false;
}
len -= bits;
offs += bits;
ptr += (offs >> 3);
offs &= 7;
return true;
}
typedef BitSliceGen<RefAny, const unsigned char> BitSlice;
static inline std::string to_hex(const BitSlice& bs) {
return bs.to_hex();
}
static inline std::string to_binary(const BitSlice& bs) {
return bs.to_binary();
}
class BitSliceWrite : public BitSliceGen<RefAny, unsigned char> {
public:
struct LengthMismatch {};
BitSliceWrite(RefAny _ref, unsigned char* _ptr, unsigned _offs, unsigned _len)
: BitSliceGen<RefAny, unsigned char>(_ref, _ptr, _offs, _len) {
}
BitSliceWrite() : BitSliceGen<RefAny, unsigned char>() {
}
BitSliceWrite(unsigned char* _ptr, unsigned _len) : BitSliceGen<RefAny, unsigned char>(_ptr, _len) {
}
operator BitSlice&() {
return *reinterpret_cast<BitSlice*>(this);
}
operator const BitSlice&() const {
return *reinterpret_cast<const BitSlice*>(this);
}
const BitSliceWrite& operator=(const BitSlice& bs) const;
const BitSliceWrite& operator=(bool val) const;
};
class BitString : public CntObject {
unsigned char* ptr;
unsigned offs, len, bytes_alloc;
public:
BitString() : ptr(0), offs(0), len(0), bytes_alloc(0) {
}
explicit BitString(const BitSlice& bs, unsigned reserve_bits = 0);
explicit BitString(unsigned reserve_bits);
BitString(const BitString&) = delete;
BitString& operator=(const BitString&) = delete;
BitString(BitString&&) = delete;
BitString& operator=(BitString&&) = delete;
~BitString() {
if (ptr) {
std::free(ptr);
}
}
operator BitSlice() const;
BitString* make_copy() const override;
unsigned size() const {
return len;
}
unsigned byte_size() const {
return (offs + len + 7) >> 3;
}
ConstBitPtr cbits() const {
return ConstBitPtr{ptr, (int)offs};
}
ConstBitPtr bits() const {
return ConstBitPtr{ptr, (int)offs};
}
BitPtr bits() {
return BitPtr{ptr, (int)offs};
}
BitString& reserve_bits(unsigned req_bits);
BitSliceWrite reserve_bitslice(unsigned req_bits);
BitString& append(const BitSlice& bs);
BitSlice subslice(unsigned from, unsigned bits) const;
BitSliceWrite subslice_write(unsigned from, unsigned bits);
std::string to_hex() const {
return bitstring::bits_to_hex(cbits(), size());
}
std::string to_binary() const {
return bitstring::bits_to_binary(cbits(), size());
}
};
extern std::ostream& operator<<(std::ostream& stream, const BitString& bs);
extern std::ostream& operator<<(std::ostream& stream, Ref<BitString> bs_ref);
extern template class Ref<BitString>;
typedef Ref<BitString> BitStringRef;
template <unsigned n>
class BitArray {
static constexpr unsigned m = (n + 7) >> 3;
typedef std::array<unsigned char, m> byte_array_t;
typedef unsigned char raw_byte_array_t[m];
byte_array_t bytes;
public:
const unsigned char* data() const {
return bytes.data();
}
unsigned char* data() {
return bytes.data();
}
unsigned size() const {
return n;
}
const byte_array_t& as_array() const {
return bytes;
}
byte_array_t& as_array() {
return bytes;
}
Slice as_slice() const {
return Slice{data(), m};
}
MutableSlice as_slice() {
return MutableSlice{data(), m};
}
ConstBitPtr cbits() const {
return ConstBitPtr{data()};
}
ConstBitPtr bits() const {
return ConstBitPtr{data()};
}
BitPtr bits() {
return BitPtr{data()};
}
BitArray() = default;
BitArray(const BitArray&) = default;
BitArray(const byte_array_t& init_bytes) : bytes(init_bytes) {
}
explicit BitArray(const raw_byte_array_t init_bytes) {
std::memcpy(data(), init_bytes, m);
}
BitArray(ConstBitPtr from) {
bitstring::bits_memcpy(bits(), from, n);
}
template <int N = n, typename X = std::enable_if_t<N == n && N <= 64, int>>
explicit BitArray(long long val) {
bitstring::bits_store_long(bits(), val, n);
}
BitArray& operator=(const BitArray&) = default;
BitArray& operator=(const byte_array_t& set_bytes) {
bytes = set_bytes;
return *this;
}
BitArray& operator=(ConstBitPtr from) {
bitstring::bits_memcpy(bits(), from, n);
return *this;
}
BitArray& operator=(const raw_byte_array_t set_byte_array) {
std::memcpy(data(), set_byte_array, m);
return *this;
}
BitSliceWrite write_bitslice() {
return BitSliceWrite{data(), n};
}
BitSlice as_bitslice() const {
return BitSlice{data(), n};
}
//operator BitString() const {
//return BitString{as_bitslice()};
//}
Ref<BitString> make_bitstring_ref() const {
return td::make_ref<BitString>(as_bitslice());
}
unsigned long long to_ulong() const {
return bitstring::bits_load_ulong(bits(), n);
}
long long to_long() const {
return bitstring::bits_load_long(bits(), n);
}
void store_ulong(unsigned long long val) {
bitstring::bits_store_long(bits(), val, n);
}
void store_long(long long val) {
bitstring::bits_store_long(bits(), val, n);
}
void set_same(bool v) {
bytes.fill(static_cast<unsigned char>(v ? -1 : 0));
}
void set_zero() {
set_same(0);
}
void set_zero_s() {
volatile uint8* p = data();
auto x = m;
while (x--) {
*p++ = 0;
}
}
void set_ones() {
set_same(1);
}
void clear() {
set_zero();
}
bool is_zero() const {
return bitstring::bits_memscan(cbits(), n, 0) == n;
}
std::string to_hex() const {
return bitstring::bits_to_hex(cbits(), size());
}
std::string to_binary() const {
return bitstring::bits_to_binary(cbits(), size());
}
int compare(const BitArray& other) const {
return (n % 8 == 0) ? std::memcmp(data(), other.data(), n / 8) : bitstring::bits_memcmp(bits(), other.bits(), n);
}
bool operator==(const BitArray& other) const {
return (n % 8 == 0) ? (bytes == other.bytes) : !bitstring::bits_memcmp(bits(), other.bits(), n);
}
bool operator!=(const BitArray& other) const {
return (n % 8 == 0) ? (bytes != other.bytes) : bitstring::bits_memcmp(bits(), other.bits(), n);
}
bool operator<(const BitArray& other) const {
return (n % 8 == 0) ? (bytes < other.bytes) : (bitstring::bits_memcmp(bits(), other.bits(), n) < 0);
}
int compare(ConstBitPtr other) const {
return bitstring::bits_memcmp(bits(), other, n);
}
bool operator==(ConstBitPtr other) const {
return !compare(other);
}
bool operator!=(ConstBitPtr other) const {
return compare(other);
}
BitPtr::BitSelector operator[](int i) {
return bits()[i];
}
bool operator[](int i) const {
return cbits()[i];
}
void compute_sha256(BitPtr to) const {
bitstring::bits_sha256(to, cbits(), n);
}
void compute_sha256(BitArray<256>& to) const {
bitstring::bits_sha256(to.bits(), cbits(), n);
}
static inline BitArray zero() {
BitArray x;
x.set_zero();
return x;
}
static inline BitArray ones() {
BitArray x;
x.set_ones();
return x;
}
BitArray operator^(const BitArray& with) const {
BitArray res;
for (unsigned i = 0; i < m; i++) {
res.bytes[i] = bytes[i] ^ with.bytes[i];
}
return res;
}
unsigned scan(bool value) const {
return (unsigned)cbits().scan(value, n);
}
unsigned count_leading_zeroes() const {
return scan(false);
}
unsigned count_matching(ConstBitPtr other) const {
std::size_t cnt;
bitstring::bits_memcmp(cbits(), other, n, &cnt);
return (unsigned)cnt;
}
unsigned count_matching(const BitArray& other) const {
return count_matching(other.bits());
}
};
using Bits256 = BitArray<256>;
using Bits128 = BitArray<128>;
template <unsigned n>
std::ostream& operator<<(std::ostream& stream, BitArray<n> bits) {
return stream << bits.to_hex();
}
template <unsigned N>
Slice as_slice(const BitArray<N>& value) {
return value.as_slice();
}
template <unsigned N>
MutableSlice as_slice(BitArray<N>& value) {
return value.as_slice();
}
template <unsigned N>
Ref<BitString> make_bitstring_ref(const BitArray<N>& value) {
return value.make_bitstring_ref();
}
} // namespace td

55
crypto/common/refcnt.cpp Normal file
View file

@ -0,0 +1,55 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "refcnt.hpp"
#include "td/utils/ScopeGuard.h"
namespace td {
namespace detail {
struct SafeDeleter {
public:
void retire(const CntObject *ptr) {
if (is_active_) {
to_delete_.push_back(ptr);
return;
}
is_active_ = true;
SCOPE_EXIT {
is_active_ = false;
};
delete ptr;
while (!to_delete_.empty()) {
auto *ptr = to_delete_.back();
to_delete_.pop_back();
delete ptr;
}
}
private:
std::vector<const CntObject *> to_delete_;
bool is_active_{false};
};
TD_THREAD_LOCAL SafeDeleter *deleter;
void safe_delete(const CntObject *ptr) {
init_thread_local<SafeDeleter>(deleter);
deleter->retire(ptr);
}
} // namespace detail
} // namespace td

474
crypto/common/refcnt.hpp Normal file
View file

@ -0,0 +1,474 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <cassert>
#include <utility>
#include <atomic>
#include <iostream>
#include "td/utils/StringBuilder.h"
#include "td/utils/logging.h"
namespace td {
template <class T>
class Ref;
class CntObject {
private:
mutable std::atomic<int> cnt_;
template <class T>
friend class Ref;
void inc() const {
cnt_.fetch_add(1, std::memory_order_relaxed);
}
bool dec() const {
return cnt_.fetch_sub(1, std::memory_order_acq_rel) == 1;
}
void inc(int cnt) const {
cnt_.fetch_add(cnt, std::memory_order_relaxed);
}
bool dec(int cnt) const {
return cnt_.fetch_sub(cnt, std::memory_order_acq_rel) == cnt;
}
public:
struct WriteError {};
CntObject() : cnt_(1) {
}
CntObject(const CntObject& other) : CntObject() {
}
CntObject(CntObject&& other) : CntObject() {
}
CntObject& operator=(const CntObject& other) {
return *this;
}
CntObject& operator=(CntObject&& other) {
return *this;
}
virtual ~CntObject() {
auto cnt = cnt_.load(std::memory_order_relaxed);
(void)cnt;
//TODO: assert(cnt == 0) will fail if object is allocated on stack
assert(cnt == 0 || cnt == 1);
}
virtual CntObject* make_copy() const {
throw WriteError();
}
bool is_unique() const {
return cnt_.load(std::memory_order_acquire) == 1;
}
int get_refcnt() const {
// use std::memory_order_acquire
return cnt_.load(std::memory_order_acquire);
}
void assert_unique() const {
assert(is_unique());
}
};
typedef Ref<CntObject> RefAny;
template <class T>
class Cnt : public CntObject {
T value;
public:
template <typename... Args>
Cnt(Args&&... args) : value(std::forward<Args>(args)...) {
///std::cout << "(N " << (void*)this << ")";
}
Cnt(const Cnt& x) : CntObject(), value(x.value) {
///std::cout << "(C)";
}
virtual ~Cnt() {
///std::cout << "(D " << (void*)this << ")";
}
T* operator->() {
return &value;
}
const T* operator->() const {
return &value;
}
T& operator*() {
return value;
}
const T& operator*() const {
return value;
}
Cnt* make_copy() const override {
///std::cout << "(c " << (const void*)this << ")";
return new Cnt{value};
}
};
template <class T>
struct RefValue {
using Type = T;
static Type& make_ref(T* ptr) {
return *ptr;
}
static const Type& make_const_ref(const T* ptr) {
return *ptr;
}
static Type* make_ptr(T* ptr) {
return ptr;
}
static const Type* make_const_ptr(const T* ptr) {
return ptr;
}
};
template <class T>
struct RefValue<Cnt<T>> {
using Type = T;
static Type& make_ref(Cnt<T>* ptr) {
return **ptr;
}
static const Type& make_const_ref(const Cnt<T>* ptr) {
return **ptr;
}
static Type* make_ptr(Cnt<T>* ptr) {
return &(**ptr);
}
static const Type* make_const_ptr(const Cnt<T>* ptr) {
return &(**ptr);
}
};
struct static_cast_ref {};
namespace detail {
void safe_delete(const CntObject* ptr);
}
template <class T>
class Ref {
T* ptr;
template <class S>
friend class Ref;
public:
struct NullRef {};
Ref() : ptr(0) {
}
//explicit Ref(bool init) : ptr(init ? new T : 0) {
//}
template <typename... Args>
explicit Ref(bool init, Args&&... args) : ptr(0) {
//assert(init);
ptr = new T(std::forward<Args>(args)...);
}
/*
explicit Ref(const T& c) : ptr(&c) {
ptr.inc();
}
*/
explicit Ref(T* pc) : ptr(pc) {
if (ptr) {
acquire_shared(ptr);
}
}
explicit Ref(const T* pc) : ptr(const_cast<T*>(pc)) {
if (ptr) {
acquire_shared(ptr);
}
}
explicit Ref(const T& obj) : ptr(obj.make_copy()) {
}
Ref(const Ref& r) : ptr(r.ptr) {
if (ptr) {
acquire_shared(ptr);
///std::cout << "(rc+ " << (const void*)ptr << ")";
}
}
Ref(Ref&& r) noexcept : ptr(std::move(r.ptr)) {
r.ptr = 0;
}
T* release() {
auto res = ptr;
ptr = nullptr;
return res;
}
struct acquire_t {};
Ref(T* ptr, acquire_t) : ptr(ptr) {
}
template <class S>
Ref(const Ref<S>& r, std::enable_if_t<std::is_base_of<T, S>::value, int> t = 0) : ptr(static_cast<T*>(r.ptr)) {
static_assert(std::is_base_of<T, S>::value, "Invalid static Ref conversion");
if (ptr) {
acquire_shared(ptr);
}
}
template <class S>
explicit Ref(const Ref<S>& r,
std::enable_if_t<!std::is_base_of<T, S>::value && std::is_base_of<S, T>::value, int> t = 0)
: ptr(dynamic_cast<T*>(r.ptr)) {
static_assert(std::is_base_of<S, T>::value, "Invalid dynamic Ref conversion");
if (ptr) {
acquire_shared(ptr);
//std::cout << "(rv+ " << (const void*)ptr << ")";
} else {
//std::cout << "(error converting " << (const void*)r.ptr << ")";
}
}
template <class S>
Ref(static_cast_ref, const Ref<S>& r, std::enable_if_t<std::is_base_of<S, T>::value, int> t = 0)
: ptr(static_cast<T*>(r.ptr)) {
static_assert(std::is_base_of<S, T>::value, "Invalid static Ref downcast");
if (r.ptr) {
acquire_shared(ptr);
} else {
ptr = nullptr;
}
}
template <class S>
Ref(Ref<S>&& r, std::enable_if_t<std::is_base_of<T, S>::value, int> t = 0) : ptr(static_cast<T*>(r.ptr)) {
static_assert(std::is_base_of<T, S>::value, "Invalid static Ref conversion");
r.ptr = nullptr;
}
template <class S>
explicit Ref(Ref<S>&& r, std::enable_if_t<!std::is_base_of<T, S>::value && std::is_base_of<S, T>::value, int> t = 0)
: ptr(dynamic_cast<T*>(r.ptr)) {
static_assert(std::is_base_of<S, T>::value, "Invalid dynamic Ref conversion");
if (!ptr && r.ptr) {
release_shared(r.ptr);
}
r.ptr = nullptr;
}
template <class S>
Ref(static_cast_ref, Ref<S>&& r, std::enable_if_t<std::is_base_of<S, T>::value, int> t = 0) noexcept
: ptr(static_cast<T*>(r.ptr)) {
static_assert(std::is_base_of<S, T>::value, "Invalid static Ref downcast");
if (r.ptr) {
r.ptr = nullptr;
} else {
ptr = nullptr;
}
}
~Ref() {
clear();
}
Ref& operator=(const Ref& r);
template <class S>
Ref& operator=(const Ref<S>& r);
Ref& operator=(Ref&& r);
template <class S>
Ref& operator=(Ref<S>&& r);
const typename RefValue<T>::Type* operator->() const {
if (!ptr) {
CHECK(ptr && "deferencing null Ref");
throw NullRef{};
}
return RefValue<T>::make_const_ptr(ptr);
}
const typename RefValue<T>::Type& operator*() const {
if (!ptr) {
CHECK(ptr && "deferencing null Ref");
throw NullRef{};
}
return RefValue<T>::make_const_ref(ptr);
}
const T* get() const {
return ptr;
}
bool is_null() const {
return ptr == 0;
}
bool not_null() const {
return ptr != 0;
}
bool is_unique() const {
if (!ptr) {
CHECK(ptr && "defererencing null Ref");
throw NullRef{};
}
return ptr->is_unique();
}
void clear() {
if (ptr) {
///std::cout << "(r- " << (const void*)ptr << ")";
release_shared(ptr);
ptr = 0;
}
}
void swap(Ref& r) {
std::swap(ptr, r.ptr);
}
Ref& operator^=(const Ref& r);
Ref& operator^=(Ref&& r);
Ref& operator&=(bool retain);
bool operator==(const Ref& r) const;
bool operator!=(const Ref& r) const;
typename RefValue<T>::Type& write();
typename RefValue<T>::Type& unique_write() const;
public:
template <class S>
static void release_shared(S* obj, int cnt = 1) {
if (obj->dec(cnt)) {
detail::safe_delete(obj);
}
}
template <class S>
static void acquire_shared(S* obj, int cnt = 1) {
obj->inc(cnt);
}
private:
void assign(T* p) {
ptr = p;
if (p) {
acquire_shared(p);
///std::cout << "(r+ " << (const void*)ptr << ")";
}
}
};
template <class T, typename... Args>
Ref<T> make_ref(Args&&... args) {
return Ref<T>{true, std::forward<Args>(args)...};
}
template <class T, typename... Args>
Ref<Cnt<T>> make_cnt_ref(Args&&... args) {
return Ref<Cnt<T>>{true, std::forward<Args>(args)...};
}
template <class T>
td::StringBuilder& operator<<(td::StringBuilder& sb, const Ref<T>& ref) {
if (ref.is_null()) {
return sb << "nullptr";
}
return sb << *ref;
}
template <class T>
Ref<T>& Ref<T>::operator=(const Ref<T>& r) {
if (ptr != r.ptr) {
clear();
assign(r.ptr);
}
return *this;
}
template <class T>
template <class S>
Ref<T>& Ref<T>::operator=(const Ref<S>& r) {
if (ptr != static_cast<T*>(r.ptr)) {
clear();
assign(r.ptr);
}
return *this;
}
template <class T>
Ref<T>& Ref<T>::operator=(Ref<T>&& r) {
clear();
ptr = r.ptr;
r.ptr = 0;
return *this;
}
template <class T>
template <class S>
Ref<T>& Ref<T>::operator=(Ref<S>&& r) {
clear();
ptr = r.ptr;
r.ptr = 0;
return *this;
}
template <class T>
typename RefValue<T>::Type& Ref<T>::write() {
if (!ptr) {
throw CntObject::WriteError();
}
if (!ptr->is_unique()) {
T* copy = dynamic_cast<T*>(ptr->make_copy());
if (!copy) {
throw CntObject::WriteError();
}
release_shared(ptr);
ptr = copy;
}
return RefValue<T>::make_ref(ptr);
}
template <class T>
typename RefValue<T>::Type& Ref<T>::unique_write() const {
if (!ptr || !ptr->is_unique()) {
throw CntObject::WriteError();
}
return RefValue<T>::make_ref(ptr);
}
template <class T>
Ref<T>& Ref<T>::operator^=(const Ref<T>& r) {
if (r.ptr && r.ptr != ptr) {
clear();
assign(r.ptr);
}
return *this;
}
template <class T>
Ref<T>& Ref<T>::operator^=(Ref<T>&& r) {
if (r.ptr && r.ptr != ptr) {
clear();
ptr = r.ptr;
r.ptr = 0;
}
return *this;
}
template <class T>
Ref<T>& Ref<T>::operator&=(bool retain) {
if (!retain && ptr) {
clear();
}
return *this;
}
template <class T>
bool Ref<T>::operator==(const Ref<T>& r) const {
return ptr == r.ptr;
}
template <class T>
bool Ref<T>::operator!=(const Ref<T>& r) const {
return ptr != r.ptr;
}
template <class T>
void swap(Ref<T>& r1, Ref<T>& r2) {
r1.swap(r2);
}
} // namespace td

321
crypto/common/refint.cpp Normal file
View file

@ -0,0 +1,321 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "common/refint.h"
#include <utility>
#include <iostream>
#include "td/utils/StringBuilder.h"
#include "td/utils/Slice.h"
namespace td {
template class Cnt<BigInt256>;
template class Ref<Cnt<BigInt256>>;
RefInt256 operator+(RefInt256 x, RefInt256 y) {
(x.write() += *y).normalize();
return x;
}
RefInt256 operator+(RefInt256 x, long long y) {
x.write().add_tiny(y).normalize();
return x;
}
RefInt256 operator-(RefInt256 x, RefInt256 y) {
(x.write() -= *y).normalize();
return x;
}
RefInt256 operator-(RefInt256 x, long long y) {
x.write().add_tiny(-y).normalize();
return x;
}
RefInt256 operator-(RefInt256 x) {
x.write().negate().normalize();
return x;
}
RefInt256 operator~(RefInt256 x) {
x.write().logical_not().normalize();
return x;
}
RefInt256 operator*(RefInt256 x, RefInt256 y) {
RefInt256 z{true, 0};
z.write().add_mul(*x, *y).normalize();
return z;
}
RefInt256 operator*(RefInt256 x, long long y) {
x.write().mul_short_opt(y).normalize();
return x;
}
RefInt256 operator/(RefInt256 x, RefInt256 y) {
RefInt256 quot{true};
x.write().mod_div(*y, quot.write());
quot.write().normalize();
return quot;
}
RefInt256 div(RefInt256 x, RefInt256 y, int round_mode) {
RefInt256 quot{true};
x.write().mod_div(*y, quot.write(), round_mode);
quot.write().normalize();
return quot;
}
RefInt256 operator%(RefInt256 x, RefInt256 y) {
BigInt256 quot;
x.write().mod_div(*y, quot);
return x;
}
RefInt256 mod(RefInt256 x, RefInt256 y, int round_mode) {
BigInt256 quot;
x.write().mod_div(*y, quot, round_mode);
return x;
}
std::pair<RefInt256, RefInt256> divmod(RefInt256 x, RefInt256 y, int round_mode) {
RefInt256 quot{true};
x.write().mod_div(*y, quot.write(), round_mode);
quot.write().normalize();
return std::make_pair(std::move(quot), std::move(x));
}
RefInt256 operator&(RefInt256 x, RefInt256 y) {
x.write() &= *y;
return x;
}
RefInt256 operator|(RefInt256 x, RefInt256 y) {
x.write() |= *y;
return x;
}
RefInt256 operator^(RefInt256 x, RefInt256 y) {
x.write() ^= *y;
return x;
}
RefInt256 operator<<(RefInt256 x, int y) {
(x.write() <<= y).normalize();
return x;
}
RefInt256 operator>>(RefInt256 x, int y) {
(x.write() >>= y).normalize();
return x;
}
RefInt256 rshift(RefInt256 x, int y, int round_mode) {
x.write().rshift(y, round_mode).normalize();
return x;
}
RefInt256& operator+=(RefInt256& x, RefInt256 y) {
(x.write() += *y).normalize();
return x;
}
RefInt256& operator+=(RefInt256& x, long long y) {
x.write().add_tiny(y).normalize();
return x;
}
RefInt256& operator-=(RefInt256& x, RefInt256 y) {
(x.write() -= *y).normalize();
return x;
}
RefInt256& operator-=(RefInt256& x, long long y) {
x.write().add_tiny(-y).normalize();
return x;
}
RefInt256& operator*=(RefInt256& x, RefInt256 y) {
RefInt256 z{true, 0};
z.write().add_mul(*x, *y).normalize();
return x = z;
}
RefInt256& operator*=(RefInt256& x, long long y) {
x.write().mul_short_opt(y).normalize();
return x;
}
RefInt256& operator/=(RefInt256& x, RefInt256 y) {
RefInt256 quot{true};
x.write().mod_div(*y, quot.write());
quot.write().normalize();
return x = quot;
}
RefInt256& operator%=(RefInt256& x, RefInt256 y) {
BigInt256 quot;
x.write().mod_div(*y, quot);
return x;
}
RefInt256& operator&=(RefInt256& x, RefInt256 y) {
x.write() &= *y;
return x;
}
RefInt256& operator|=(RefInt256& x, RefInt256 y) {
x.write() |= *y;
return x;
}
RefInt256& operator^=(RefInt256& x, RefInt256 y) {
x.write() ^= *y;
return x;
}
RefInt256& operator<<=(RefInt256& x, int y) {
(x.write() <<= y).normalize();
return x;
}
RefInt256& operator>>=(RefInt256& x, int y) {
(x.write() >>= y).normalize();
return x;
}
int cmp(RefInt256 x, RefInt256 y) {
return x->cmp(*y);
}
int cmp(RefInt256 x, long long y) {
return x->cmp(y);
}
int sgn(RefInt256 x) {
return x->sgn();
}
extern RefInt256 make_refint(long long x) {
auto xx = td::RefInt256{true, x};
xx.unique_write().normalize();
return xx;
}
std::string dec_string(RefInt256 x) {
return x.is_null() ? "(null)" : (x.is_unique() ? x.unique_write().to_dec_string_destroy() : x->to_dec_string());
}
std::string dec_string2(RefInt256&& x) {
return x.is_null() ? "(null)" : (x.is_unique() ? x.unique_write().to_dec_string_destroy() : x->to_dec_string());
}
std::string hex_string(RefInt256 x, bool upcase) {
return x.is_null() ? "(null)" : x->to_hex_string(upcase);
}
std::string binary_string(RefInt256 x) {
return x.is_null() ? "(null)" : x->to_binary_string();
}
std::ostream& operator<<(std::ostream& os, const RefInt256& x) {
//std::cout << "<a|";
return os << dec_string(std::move(x));
//std::cout << "|a>";
//return os;
}
std::ostream& operator<<(std::ostream& os, RefInt256&& x) {
//std::cout << "<A|";
return os << dec_string2(std::move(x));
//std::cout << "|A>";
//return os;
}
StringBuilder& operator<<(StringBuilder& sb, const RefInt256& x) {
return sb << dec_string(x);
}
RefInt256 dec_string_to_int256(const std::string& s) {
return dec_string_to_int256(td::Slice{s});
}
RefInt256 dec_string_to_int256(td::Slice s) {
if (s.size() > 255) {
return {};
}
RefInt256 x{true};
if (x.unique_write().parse_dec(s.begin(), (int)s.size()) == (int)s.size()) {
return x;
} else {
return {};
}
}
RefInt256 hex_string_to_int256(const std::string& s) {
return hex_string_to_int256(td::Slice{s});
}
RefInt256 hex_string_to_int256(td::Slice s) {
if (s.size() > 255) {
return {};
}
RefInt256 x{true};
if (x.unique_write().parse_hex(s.begin(), (int)s.size()) == (int)s.size()) {
return x;
} else {
return {};
}
}
RefInt256 string_to_int256(const std::string& s) {
return string_to_int256(td::Slice{s});
}
RefInt256 string_to_int256(td::Slice s) {
if (s.size() >= 3 && s[0] == '-' && s[1] == '0' && s[2] == 'x') {
auto x = hex_string_to_int256(td::Slice(s.begin() + 3, s.end()));
if (x.not_null()) {
x.write().negate();
}
return x;
} else if (s.size() >= 2 && s[0] == '0' && s[1] == 'x') {
return hex_string_to_int256(td::Slice(s.begin() + 2, s.end()));
} else {
return dec_string_to_int256(s);
}
}
namespace literals {
RefInt256 operator""_ri256(const char* str, std::size_t str_len) {
RefInt256 x{true};
x->enforce(x.unique_write().parse_dec(str, (int)str_len) == (int)str_len);
return x;
}
RefInt256 operator""_rx256(const char* str, std::size_t str_len) {
RefInt256 x{true};
x->enforce(x.unique_write().parse_hex(str, (int)str_len) == (int)str_len);
return x;
}
} // namespace literals
} // namespace td

127
crypto/common/refint.h Normal file
View file

@ -0,0 +1,127 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "common/refcnt.hpp"
#include "common/bigint.hpp"
#include <utility>
#include <string>
namespace td {
class StringBuilder;
extern template class Cnt<BigInt256>;
extern template class Ref<Cnt<BigInt256>>;
typedef Cnt<BigInt256> CntInt256;
typedef Ref<CntInt256> RefInt256;
extern RefInt256 operator+(RefInt256 x, RefInt256 y);
extern RefInt256 operator+(RefInt256 x, long long y);
extern RefInt256 operator-(RefInt256 x, RefInt256 y);
extern RefInt256 operator-(RefInt256 x, long long y);
extern RefInt256 operator*(RefInt256 x, RefInt256 y);
extern RefInt256 operator*(RefInt256 x, long long y);
extern RefInt256 operator/(RefInt256 x, RefInt256 y);
extern RefInt256 operator%(RefInt256 x, RefInt256 y);
extern RefInt256 div(RefInt256 x, RefInt256 y, int round_mode = -1);
extern RefInt256 mod(RefInt256 x, RefInt256 y, int round_mode = -1);
extern std::pair<RefInt256, RefInt256> divmod(RefInt256 x, RefInt256 y, int round_mode = -1);
extern RefInt256 operator-(RefInt256 x);
extern RefInt256 operator&(RefInt256 x, RefInt256 y);
extern RefInt256 operator|(RefInt256 x, RefInt256 y);
extern RefInt256 operator^(RefInt256 x, RefInt256 y);
extern RefInt256 operator~(RefInt256 x);
extern RefInt256 operator<<(RefInt256 x, int y);
extern RefInt256 operator>>(RefInt256 x, int y);
extern RefInt256 rshift(RefInt256 x, int y, int round_mode = -1);
extern RefInt256& operator+=(RefInt256& x, RefInt256 y);
extern RefInt256& operator+=(RefInt256& x, long long y);
extern RefInt256& operator-=(RefInt256& x, RefInt256 y);
extern RefInt256& operator-=(RefInt256& x, long long y);
extern RefInt256& operator*=(RefInt256& x, RefInt256 y);
extern RefInt256& operator*=(RefInt256& x, long long y);
extern RefInt256& operator/=(RefInt256& x, RefInt256 y);
extern RefInt256& operator%=(RefInt256& x, RefInt256 y);
extern RefInt256& operator&=(RefInt256& x, RefInt256 y);
extern RefInt256& operator|=(RefInt256& x, RefInt256 y);
extern RefInt256& operator^=(RefInt256& x, RefInt256 y);
extern RefInt256& operator<<=(RefInt256& x, int y);
extern RefInt256& operator>>=(RefInt256& x, int y);
template <typename T>
bool operator==(RefInt256 x, T y) {
return cmp(x, y) == 0;
}
template <typename T>
bool operator!=(RefInt256 x, T y) {
return cmp(x, y) != 0;
}
template <typename T>
bool operator<(RefInt256 x, T y) {
return cmp(x, y) < 0;
}
template <typename T>
bool operator>(RefInt256 x, T y) {
return cmp(x, y) > 0;
}
template <typename T>
bool operator<=(RefInt256 x, T y) {
return cmp(x, y) <= 0;
}
template <typename T>
bool operator>=(RefInt256 x, T y) {
return cmp(x, y) >= 0;
}
extern int cmp(RefInt256 x, RefInt256 y);
extern int cmp(RefInt256 x, long long y);
extern int sgn(RefInt256 x);
extern RefInt256 make_refint(long long x);
extern std::string dec_string(RefInt256 x);
extern std::string dec_string2(RefInt256&& x);
extern std::string hex_string(RefInt256 x, bool upcase = false);
extern std::string binary_string(RefInt256 x);
extern RefInt256 dec_string_to_int256(const std::string& s);
extern RefInt256 dec_string_to_int256(td::Slice s);
extern RefInt256 hex_string_to_int256(const std::string& s);
extern RefInt256 hex_string_to_int256(td::Slice s);
extern RefInt256 string_to_int256(const std::string& s);
extern RefInt256 string_to_int256(td::Slice s);
extern std::ostream& operator<<(std::ostream& os, const RefInt256& x);
extern std::ostream& operator<<(std::ostream& os, RefInt256&& x);
extern td::StringBuilder& operator<<(td::StringBuilder& os, const RefInt256& x);
namespace literals {
extern RefInt256 operator""_ri256(const char* str, std::size_t str_len);
extern RefInt256 operator""_rx256(const char* str, std::size_t str_len);
} // namespace literals
} // namespace td

212
crypto/common/util.cpp Normal file
View file

@ -0,0 +1,212 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "util.h"
#include <limits>
namespace td {
std::size_t compute_base64_encoded_size(size_t bindata_size) {
return ((bindata_size + 2) / 3) << 2;
}
const char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const char base64_url_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
const unsigned char base64_dec_table[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0x7e, 0, 0xbe, 0, 0x7f, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
0xfd, 0, 0, 0, 1, 0, 0, 0, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0, 0, 0, 0,
0xbf, 0, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0, 0, 0, 0, 0};
std::size_t buff_base64_encode(td::MutableSlice buffer, td::Slice raw, bool base64_url) {
std::size_t orig_size = raw.size(), res_size = compute_base64_encoded_size(orig_size);
if (res_size > buffer.size()) {
return 0;
}
const char *table = base64_url ? base64_url_table : base64_table;
char *wptr = buffer.data();
unsigned x;
std::size_t i;
for (i = 0; i < orig_size - 2; i += 3) {
x = (((unsigned)(unsigned char)raw[i]) << 16) | (((unsigned)(unsigned char)raw[i + 1]) << 8) |
((unsigned)(unsigned char)raw[i + 2]);
*wptr++ = table[(x >> 18) & 0x3f];
*wptr++ = table[(x >> 12) & 0x3f];
*wptr++ = table[(x >> 6) & 0x3f];
*wptr++ = table[x & 0x3f];
}
switch (orig_size - i) {
case 1:
x = (((unsigned)(unsigned char)raw[i]) << 16);
*wptr++ = table[(x >> 18) & 0x3f];
*wptr++ = table[(x >> 12) & 0x3f];
*wptr++ = '=';
*wptr++ = '=';
break;
case 2:
x = (((unsigned)(unsigned char)raw[i]) << 16) | (((unsigned)(unsigned char)raw[i + 1]) << 8);
*wptr++ = table[(x >> 18) & 0x3f];
*wptr++ = table[(x >> 12) & 0x3f];
*wptr++ = table[(x >> 6) & 0x3f];
*wptr++ = '=';
}
CHECK(wptr == buffer.data() + res_size);
return res_size;
}
std::string str_base64_encode(std::string raw, bool base64_url) {
return str_base64_encode(td::Slice{raw}, base64_url);
}
std::string str_base64_encode(td::Slice raw, bool base64_url) {
std::size_t res_size = compute_base64_encoded_size(raw.size());
std::string s;
s.resize(res_size);
if (res_size) {
buff_base64_encode(td::MutableSlice{const_cast<char *>(s.data()), s.size()}, raw, base64_url);
}
return s;
}
bool is_valid_base64(std::string encoded, bool allow_base64_url) {
return is_valid_base64(td::Slice{encoded}, allow_base64_url);
}
bool is_valid_base64(td::Slice encoded, bool allow_base64_url) {
const unsigned char *ptr = (const unsigned char *)encoded.data(), *end = ptr + encoded.size();
if (encoded.size() & 3) {
return false;
}
unsigned mode = (allow_base64_url ? 0xc0 : 0x40);
while (ptr < end && (base64_dec_table[*ptr] & mode)) {
ptr++;
}
std::size_t d = end - ptr;
if (d > 2) {
return false;
}
while (ptr < end && *ptr == '=') {
ptr++;
}
return ptr == end;
}
td::int32 decoded_base64_size(std::string encoded, bool allow_base64_url) {
return decoded_base64_size(td::Slice{encoded}, allow_base64_url);
}
td::int32 decoded_base64_size(td::Slice encoded, bool allow_base64_url) {
const unsigned char *ptr = (const unsigned char *)encoded.data(), *end = ptr + encoded.size();
if (encoded.size() & 3) {
return -1;
}
if (encoded.size() > static_cast<size_t>(std::numeric_limits<td::int32>::max())) {
return -1;
}
if (end == ptr) {
return 0;
}
auto s = static_cast<td::int32>((encoded.size() >> 2) * 3);
if (end[-1] == '=') {
s--;
if (end[-2] == '=') {
s--;
}
}
return s;
}
std::size_t buff_base64_decode(td::MutableSlice buffer, td::Slice encoded, bool allow_base64_url) {
if ((encoded.size() & 3) || !encoded.size()) {
return 0;
}
std::size_t n = (encoded.size() >> 2);
const unsigned char *ptr = (const unsigned char *)encoded.data(), *end = ptr + encoded.size();
unsigned q = (end[-1] == '=' ? (end[-2] == '=' ? 2 : 1) : 0);
if (buffer.size() + q < n * 3) {
return 0;
}
unsigned char *wptr = (unsigned char *)buffer.data(), *wend = wptr + buffer.size();
unsigned mode = (allow_base64_url ? 0xc0 : 0x40);
for (std::size_t i = 0; i < n; i++) {
unsigned x = 0;
for (std::size_t j = 0; j < 4; j++) {
unsigned z = base64_dec_table[ptr[4 * i + j]];
if (!(z & mode) && z != 1 && (i < n - 1 || j + q < 4)) {
return 0;
}
x = (x << 6) | (z & 0x3f);
}
if (i < n - 1) {
*wptr++ = (unsigned char)(x >> 16);
*wptr++ = (unsigned char)(x >> 8);
*wptr++ = (unsigned char)x;
} else {
for (; q < 3; q++) {
*wptr++ = (unsigned char)(x >> 16);
x <<= 8;
}
break;
}
}
CHECK(wptr <= wend);
return wptr - (unsigned char *)buffer.data();
}
td::BufferSlice base64_decode(std::string encoded, bool allow_base64_url) {
return base64_decode(td::Slice{encoded}, allow_base64_url);
}
td::BufferSlice base64_decode(td::Slice encoded, bool allow_base64_url) {
auto s = decoded_base64_size(encoded, allow_base64_url);
if (s <= 0) {
return td::BufferSlice{};
}
td::BufferSlice res{static_cast<std::size_t>(s)};
auto r = buff_base64_decode(res.as_slice(), encoded, allow_base64_url);
if (!r) {
return td::BufferSlice{};
}
CHECK(r == static_cast<std::size_t>(s));
return res;
}
std::string str_base64_decode(std::string encoded, bool allow_base64_url) {
return str_base64_decode(td::Slice{encoded}, allow_base64_url);
}
std::string str_base64_decode(td::Slice encoded, bool allow_base64_url) {
auto s = decoded_base64_size(encoded, allow_base64_url);
if (s <= 0) {
return std::string{};
}
std::string res;
res.resize(static_cast<std::size_t>(s));
auto r = buff_base64_decode(td::MutableSlice{const_cast<char *>(res.data()), res.size()}, encoded, allow_base64_url);
if (!r) {
return std::string{};
}
CHECK(r == static_cast<std::size_t>(s));
return res;
}
} // namespace td

42
crypto/common/util.h Normal file
View file

@ -0,0 +1,42 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <string>
#include "td/utils/Slice.h"
#include "td/utils/buffer.h"
namespace td {
std::size_t compute_base64_encoded_size(size_t bindata_size);
std::size_t buff_base64_encode(td::MutableSlice buffer, td::Slice raw, bool base64_url = false);
std::string str_base64_encode(std::string raw, bool base64_url = false);
std::string str_base64_encode(td::Slice raw, bool base64_url = false);
bool is_valid_base64(std::string encoded, bool allow_base64_url = true);
bool is_valid_base64(td::Slice encoded, bool allow_base64_url = true);
td::int32 decoded_base64_size(std::string encoded, bool allow_base64_url = true);
td::int32 decoded_base64_size(td::Slice encoded, bool allow_base64_url = true);
std::size_t buff_base64_decode(td::MutableSlice buffer, td::Slice data, bool allow_base64_url = true);
td::BufferSlice base64_decode(std::string encoded, bool allow_base64_url = true);
td::BufferSlice base64_decode(td::Slice encoded, bool allow_base64_url = true);
std::string str_base64_decode(std::string encoded, bool allow_base64_url = true);
std::string str_base64_decode(td::Slice encoded, bool allow_base64_url = true);
} // namespace td

280
crypto/ellcurve/Ed25519.cpp Normal file
View file

@ -0,0 +1,280 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "Ed25519.h"
#include "td/utils/Random.h"
namespace crypto {
namespace Ed25519 {
bool all_bytes_same(const unsigned char *str, std::size_t size) {
unsigned char c = str[0];
for (std::size_t i = 0; i < size; i++) {
if (str[i] != c) {
return false;
}
}
return true;
}
void PublicKey::clear(void) {
if (inited != pk_empty) {
std::memset(pubkey, 0, pubkey_bytes);
PubKey.zeroize();
PubKey_xz.zeroize();
}
inited = pk_empty;
}
PublicKey::PublicKey(const unsigned char pub_key[pubkey_bytes])
: inited(pk_empty), PubKey(ellcurve::Fp25519()), PubKey_xz(ellcurve::Fp25519()) {
import_public_key(pub_key);
}
PublicKey::PublicKey(const ellcurve::TwEdwardsCurve::SegrePoint &Pub_Key)
: inited(pk_empty), PubKey(ellcurve::Fp25519()), PubKey_xz(ellcurve::Fp25519()) {
import_public_key(Pub_Key);
}
bool PublicKey::import_public_key(const unsigned char pub_key[pubkey_bytes]) {
clear();
if (all_bytes_same(pub_key, pubkey_bytes)) {
return false;
}
bool ok = false;
PubKey = ellcurve::Ed25519().import_point(pub_key, ok);
if (!ok) {
clear();
return false;
}
std::memcpy(pubkey, pub_key, pubkey_bytes);
PubKey_xz.X = PubKey.Z + PubKey.Y;
PubKey_xz.Z = PubKey.Z - PubKey.Y;
inited = pk_init;
return true;
}
bool PublicKey::import_public_key(const ellcurve::TwEdwardsCurve::SegrePoint &Pub_Key) {
clear();
if (!Pub_Key.is_valid()) {
return false;
}
PubKey = Pub_Key;
PubKey_xz.X = PubKey.Z + PubKey.Y;
PubKey_xz.Z = PubKey.Z - PubKey.Y;
inited = pk_init;
if (!PubKey.export_point(pubkey)) {
clear();
return false;
}
return true;
}
bool PublicKey::export_public_key(unsigned char pubkey_buffer[pubkey_bytes]) const {
if (inited != pk_init) {
std::memset(pubkey_buffer, 0, pubkey_bytes);
return false;
} else {
std::memcpy(pubkey_buffer, pubkey, pubkey_bytes);
return true;
}
}
bool PublicKey::check_message_signature(const unsigned char signature[sign_bytes], const unsigned char *message,
std::size_t msg_size) {
if (inited != pk_init) {
return false;
}
unsigned char hash[64];
{
digest::SHA512 hasher(signature, 32);
hasher.feed(pubkey, 32);
hasher.feed(message, msg_size);
hasher.extract(hash);
}
auto &E = ellcurve::Ed25519();
const arith::Bignum &L = E.get_ell();
arith::Bignum H, S;
S.import_lsb(signature + 32, 32);
H.import_lsb(hash, 64);
H %= L;
H = L - H;
auto sG = E.power_gen(S);
auto hA = E.power_point(PubKey, H);
auto pR1 = E.add_points(sG, hA);
unsigned char pR1_bytes[32];
if (!pR1.export_point(pR1_bytes)) {
return false;
}
return !std::memcmp(pR1_bytes, signature, 32);
}
// ---------------------
class PrivateKey;
bool PrivateKey::random_private_key(bool strong) {
inited = false;
if (!prng::rand_gen().rand_bytes(privkey, privkey_bytes, strong)) {
clear();
return false;
}
return process_private_key();
}
void PrivateKey::clear(void) {
std::memset(privkey, 0, privkey_bytes);
std::memset(priv_salt, 0, sizeof(priv_salt));
priv_exp.clear();
PubKey.clear();
inited = false;
}
bool PrivateKey::import_private_key(const unsigned char pk[privkey_bytes]) {
clear();
if (all_bytes_same(pk, privkey_bytes)) {
return false;
}
std::memcpy(privkey, pk, privkey_bytes);
return process_private_key();
}
bool PrivateKey::export_private_key(unsigned char pk[privkey_bytes]) const { // careful!
if (!inited) {
std::memset(pk, 0, privkey_bytes);
return false;
} else {
std::memcpy(pk, privkey, privkey_bytes);
return true;
}
}
bool PrivateKey::process_private_key() {
unsigned char buff[64];
digest::hash_str<digest::SHA512>(buff, privkey, privkey_bytes);
std::memcpy(priv_salt, buff + 32, 32);
buff[0] = (unsigned char)(buff[0] & -8);
buff[31] = (unsigned char)((buff[31] | 0x40) & ~0x80);
priv_exp.import_lsb(buff, 32);
PubKey = ellcurve::Ed25519().power_gen(priv_exp, true); // uniform
inited = PubKey.ok();
if (!inited) {
clear();
}
return inited;
}
bool PrivateKey::compute_shared_secret(unsigned char secret[shared_secret_bytes], const PublicKey &Pub) {
if (!inited || !Pub.ok()) {
std::memset(secret, 0, shared_secret_bytes);
*(long *)secret = static_cast<long>(td::Random::fast_uint64());
return false;
}
// uniform power!
auto P = ellcurve::Curve25519().power_xz(Pub.get_point_xz(), priv_exp);
if (P.is_infty()) {
std::memset(secret, 0, shared_secret_bytes);
*(long *)secret = static_cast<long>(td::Random::fast_uint64());
return false;
}
P.export_point_u(secret);
return true;
}
bool PrivateKey::compute_temp_shared_secret(unsigned char secret[shared_secret_bytes],
const unsigned char temp_pub_key[pubkey_bytes]) {
PublicKey tempPubkey(temp_pub_key);
if (!tempPubkey.ok()) {
return false;
}
return compute_shared_secret(secret, tempPubkey);
}
bool PrivateKey::sign_message(unsigned char signature[sign_bytes], const unsigned char *message, std::size_t msg_size) {
if (!inited) {
std::memset(signature, 0, sign_bytes);
return false;
}
unsigned char r_bytes[64];
digest::hash_two_str<digest::SHA512>(r_bytes, priv_salt, 32, message, msg_size);
const arith::Bignum &L = ellcurve::Ed25519().get_ell();
arith::Bignum eR;
eR.import_lsb(r_bytes, 64);
eR %= L;
std::memset(r_bytes, 0, sizeof(r_bytes));
// uniform power
auto pR = ellcurve::Ed25519().power_gen(eR, true);
auto ok = pR.export_point(signature, true);
(void)ok;
assert(ok);
{
digest::SHA512 hasher(signature, 32);
hasher.feed(PubKey.get_pubkey_ptr(), 32);
hasher.feed(message, msg_size);
hasher.extract(r_bytes);
}
arith::Bignum S;
S.import_lsb(r_bytes, 64);
S %= L;
S *= priv_exp;
S += eR;
S %= L;
eR.clear();
S.export_lsb(signature + 32, 32);
return true;
}
// ---------------------------------
class TempKeyGenerator;
unsigned char *TempKeyGenerator::get_temp_private_key(unsigned char *to, const unsigned char *message, std::size_t size,
const unsigned char *rand,
std::size_t rand_size) { // rand may be 0
digest::SHA256 hasher(message, size);
hasher.feed(random_salt, salt_size);
if (rand && rand_size) {
hasher.feed(rand, rand_size);
}
if (!to) {
to = buffer;
}
hasher.extract(to);
//++ *((long *)random_salt);
return to;
}
void TempKeyGenerator::create_temp_private_key(PrivateKey &pk, const unsigned char *message, std::size_t size,
const unsigned char *rand, std::size_t rand_size) {
pk.import_private_key(get_temp_private_key(buffer, message, size, rand, rand_size));
std::memset(buffer, 0, privkey_bytes);
}
bool TempKeyGenerator::create_temp_shared_secret(unsigned char temp_pub_key[pubkey_bytes],
unsigned char shared_secret[shared_secret_bytes],
const PublicKey &recipientPubKey, const unsigned char *message,
std::size_t size, const unsigned char *rand, std::size_t rand_size) {
PrivateKey tmpPk;
create_temp_private_key(tmpPk, message, size, rand, rand_size);
return tmpPk.export_public_key(temp_pub_key) && tmpPk.compute_shared_secret(shared_secret, recipientPubKey);
}
} // namespace Ed25519
} // namespace crypto

188
crypto/ellcurve/Ed25519.h Normal file
View file

@ -0,0 +1,188 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "ellcurve/Montgomery.h"
#include "ellcurve/TwEdwards.h"
#include "openssl/digest.h"
#include "openssl/rand.hpp"
#include <assert.h>
#include <cstring>
#include "td/utils/buffer.h"
namespace crypto {
namespace Ed25519 {
const int privkey_bytes = 32;
const int pubkey_bytes = 32;
const int sign_bytes = 64;
const int shared_secret_bytes = 32;
class PublicKey {
enum { pk_empty, pk_xz, pk_init } inited;
unsigned char pubkey[pubkey_bytes];
ellcurve::TwEdwardsCurve::SegrePoint PubKey;
ellcurve::MontgomeryCurve::PointXZ PubKey_xz;
public:
PublicKey() : inited(pk_empty), PubKey(ellcurve::Fp25519()), PubKey_xz(ellcurve::Fp25519()) {
}
PublicKey(const unsigned char pub_key[pubkey_bytes]);
PublicKey(td::Slice pub_key) : PublicKey(pub_key.ubegin()) {
CHECK(pub_key.size() == pubkey_bytes);
}
PublicKey(const ellcurve::TwEdwardsCurve::SegrePoint &Pub_Key);
bool import_public_key(const unsigned char pub_key[pubkey_bytes]);
bool import_public_key(td::Slice pub_key) {
CHECK(pub_key.size() == pubkey_bytes);
return import_public_key(pub_key.ubegin());
}
bool import_public_key(const ellcurve::TwEdwardsCurve::SegrePoint &Pub_Key);
bool export_public_key(unsigned char pubkey_buffer[pubkey_bytes]) const;
bool export_public_key(td::MutableSlice pubk) const {
CHECK(pubk.size() == pubkey_bytes);
return export_public_key(pubk.ubegin());
}
bool check_message_signature(const unsigned char signature[sign_bytes], const unsigned char *message,
std::size_t msg_size);
bool check_message_signature(td::Slice signature, td::Slice message) {
CHECK(signature.size() == sign_bytes);
return check_message_signature(signature.ubegin(), message.ubegin(), message.size());
}
void clear();
bool ok() const {
return inited == pk_init;
}
const unsigned char *get_pubkey_ptr() const {
return inited == pk_init ? pubkey : 0;
}
const ellcurve::TwEdwardsCurve::SegrePoint &get_point() const {
return PubKey;
}
const ellcurve::MontgomeryCurve::PointXZ &get_point_xz() const {
return PubKey_xz;
}
};
class PrivateKey {
public:
struct priv_key_no_copy {};
PrivateKey() : inited(false) {
memset(privkey, 0, privkey_bytes);
}
PrivateKey(const unsigned char pk[privkey_bytes]) : inited(false) {
memset(privkey, 0, privkey_bytes);
import_private_key(pk);
}
PrivateKey(td::Slice pk) : inited(false) {
CHECK(pk.size() == privkey_bytes);
memset(privkey, 0, privkey_bytes);
import_private_key(pk.ubegin());
}
~PrivateKey() {
clear();
}
bool random_private_key(bool strong = false);
bool import_private_key(const unsigned char pk[privkey_bytes]);
bool import_private_key(td::Slice pk) {
CHECK(pk.size() == privkey_bytes);
return import_private_key(pk.ubegin());
}
bool export_private_key(unsigned char pk[privkey_bytes]) const; // careful!
bool export_private_key(td::MutableSlice pk) const { // careful!
return export_private_key(pk.ubegin());
}
bool export_public_key(unsigned char pubk[pubkey_bytes]) const {
return PubKey.export_public_key(pubk);
}
bool export_public_key(td::MutableSlice pubk) const {
return PubKey.export_public_key(pubk);
}
void clear();
bool ok() const {
return inited;
}
// used for EdDSA (sign)
bool sign_message(unsigned char signature[sign_bytes], const unsigned char *message, std::size_t msg_size);
bool sign_message(td::MutableSlice signature, td::Slice message) {
CHECK(signature.size() == sign_bytes);
return sign_message(signature.ubegin(), message.ubegin(), message.size());
}
// used for ECDH (encrypt / decrypt)
bool compute_shared_secret(unsigned char secret[shared_secret_bytes], const PublicKey &Pub);
bool compute_shared_secret(td::MutableSlice secret, const PublicKey &Pub) {
CHECK(secret.size() == shared_secret_bytes);
return compute_shared_secret(secret.ubegin(), Pub);
}
// used for EC asymmetric decryption
bool compute_temp_shared_secret(unsigned char secret[shared_secret_bytes],
const unsigned char temp_pub_key[pubkey_bytes]);
const PublicKey &get_public_key() const {
return PubKey;
}
private:
bool inited;
unsigned char privkey[privkey_bytes];
unsigned char priv_salt[32];
arith::Bignum priv_exp;
PublicKey PubKey;
bool process_private_key();
PrivateKey(const PrivateKey &) {
throw priv_key_no_copy();
}
PrivateKey &operator=(const PrivateKey &) {
throw priv_key_no_copy();
}
};
// use one TempKeyGenerator object a lot of times
class TempKeyGenerator {
enum { salt_size = 64 };
unsigned char random_salt[salt_size];
unsigned char buffer[privkey_bytes];
public:
TempKeyGenerator() {
prng::rand_gen().strong_rand_bytes(random_salt, salt_size);
}
~TempKeyGenerator() {
memset(random_salt, 0, salt_size);
memset(buffer, 0, privkey_bytes);
}
unsigned char *get_temp_private_key(unsigned char *to, const unsigned char *message, std::size_t size,
const unsigned char *rand = 0, std::size_t rand_size = 0); // rand may be 0
void create_temp_private_key(PrivateKey &pk, const unsigned char *message, std::size_t size,
const unsigned char *rand = 0, std::size_t rand_size = 0);
// sets temp_pub_key and shared_secret for one-time asymmetric encryption of message
bool create_temp_shared_secret(unsigned char temp_pub_key[pubkey_bytes], unsigned char secret[shared_secret_bytes],
const PublicKey &recipientPubKey, const unsigned char *message, std::size_t size,
const unsigned char *rand = 0, std::size_t rand_size = 0);
};
} // namespace Ed25519
} // namespace crypto

View file

@ -0,0 +1,33 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "ellcurve/Fp25519.h"
namespace ellcurve {
using namespace arith;
const Bignum& P25519() {
static const Bignum P25519 = (Bignum(1) << 255) - 19;
return P25519;
}
td::Ref<ResidueRing> Fp25519() {
static const td::Ref<ResidueRing> Fp25519(true, P25519());
return Fp25519;
}
} // namespace ellcurve

32
crypto/ellcurve/Fp25519.h Normal file
View file

@ -0,0 +1,32 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "common/refcnt.hpp"
#include "openssl/residue.h"
namespace ellcurve {
using namespace arith;
// returns 2^255-19
const Bignum& P25519();
// residue ring modulo P25519
td::Ref<ResidueRing> Fp25519();
} // namespace ellcurve

View file

@ -0,0 +1,138 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "ellcurve/Montgomery.h"
#include <assert.h>
#include <cstring>
namespace ellcurve {
using namespace arith;
class MontgomeryCurve;
void MontgomeryCurve::init() {
assert(!((a_short + 2) & 3) && a_short >= 0);
}
void MontgomeryCurve::set_order_cofactor(const Bignum& order, int cof) {
assert(order > 0);
assert(cof >= 0);
assert(cof == 0 || (order % cof) == 0);
Order = order;
cofactor = cofactor_short = cof;
if (cof > 0) {
L = order / cof;
assert(is_prime(L));
}
assert(!power_gen_xz(1).is_infty());
assert(power_gen_xz(Order).is_infty());
}
// computes u(P+Q)*u(P-Q) as X/Z
MontgomeryCurve::PointXZ MontgomeryCurve::add_xz(const MontgomeryCurve::PointXZ& P,
const MontgomeryCurve::PointXZ& Q) const {
Residue u = (P.X + P.Z) * (Q.X - Q.Z);
Residue v = (P.X - P.Z) * (Q.X + Q.Z);
return MontgomeryCurve::PointXZ(sqr(u + v), sqr(u - v));
}
// computes u(2P) as X/Z
MontgomeryCurve::PointXZ MontgomeryCurve::double_xz(const MontgomeryCurve::PointXZ& P) const {
Residue u = sqr(P.X + P.Z);
Residue v = sqr(P.X - P.Z);
Residue w = u - v;
return PointXZ(u * v, w * (v + Residue(a_short, ring) * w));
}
MontgomeryCurve::PointXZ MontgomeryCurve::power_gen_xz(const Bignum& n) const {
return power_xz(Gu, n);
}
MontgomeryCurve::PointXZ MontgomeryCurve::power_xz(const Residue& u, const Bignum& n) const {
return power_xz(PointXZ(u), n);
}
// computes u([n]P) in form X/Z
MontgomeryCurve::PointXZ MontgomeryCurve::power_xz(const PointXZ& A, const Bignum& n) const {
assert(n >= 0);
if (n == 0) {
return PointXZ(ring);
}
int k = n.num_bits();
PointXZ P(A);
PointXZ Q(double_xz(P));
for (int i = k - 2; i >= 0; --i) {
PointXZ PQ(add_xz(P, Q));
PQ.X *= A.Z;
PQ.Z *= A.X;
if (n[i]) {
P = PQ;
Q = double_xz(Q);
} else {
Q = PQ;
P = double_xz(P);
}
}
return P;
}
bool MontgomeryCurve::PointXZ::export_point_y(unsigned char buffer[32]) const {
if ((X + Z).is_zero()) {
std::memset(buffer, 0xff, 32);
return false;
} else {
get_y().extract().export_lsb(buffer, 32);
return true;
}
}
bool MontgomeryCurve::PointXZ::export_point_u(unsigned char buffer[32]) const {
if (Z.is_zero()) {
std::memset(buffer, 0xff, 32);
return false;
} else {
get_u().extract().export_lsb(buffer, 32);
return true;
}
}
MontgomeryCurve::PointXZ MontgomeryCurve::import_point_u(const unsigned char point[32]) const {
Bignum u;
u.import_lsb(point, 32);
u[255] = 0;
return PointXZ(Residue(u, ring));
}
MontgomeryCurve::PointXZ MontgomeryCurve::import_point_y(const unsigned char point[32]) const {
Bignum y;
y.import_lsb(point, 32);
y[255] = 0;
return PointXZ(Residue(y, ring), true);
}
const MontgomeryCurve& Curve25519() {
static const MontgomeryCurve Curve25519 = [] {
MontgomeryCurve res(486662, 9, Fp25519());
res.set_order_cofactor(hex_string{"80000000000000000000000000000000a6f7cef517bce6b2c09318d2e7ae9f68"}, 8);
return res;
}();
return Curve25519;
}
} // namespace ellcurve

View file

@ -0,0 +1,123 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <iostream>
#include <string>
#include "openssl/bignum.h"
#include "openssl/residue.h"
#include "ellcurve/Fp25519.h"
namespace ellcurve {
using namespace arith;
class MontgomeryCurve {
td::Ref<ResidueRing> ring;
int A_short; // v^2 = u^2 + Au + 1
int Gu_short; // u(G)
int a_short; // (A+2)/4
Residue A_;
Residue Gu;
Bignum P_;
Bignum L;
Bignum Order;
Bignum cofactor;
int cofactor_short;
void init();
public:
MontgomeryCurve(int _A, int _Gu, td::Ref<ResidueRing> _R)
: ring(_R)
, A_short(_A)
, Gu_short(_Gu)
, a_short((_A + 2) / 4)
, A_(_A, _R)
, Gu(_Gu, _R)
, P_(_R->get_modulus())
, cofactor_short(0) {
init();
}
const Residue& get_gen_u() const {
return Gu;
}
const Bignum& get_ell() const {
return L;
}
const Bignum& get_order() const {
return Order;
}
td::Ref<ResidueRing> get_base_ring() const {
return ring;
}
const Bignum& get_p() const {
return P_;
}
void set_order_cofactor(const Bignum& order, int cof);
struct PointXZ {
Residue X, Z;
PointXZ(Residue x, Residue z) : X(x), Z(z) {
x.same_ring(z);
}
PointXZ(td::Ref<ResidueRing> r) : X(r->one()), Z(r->zero()) {
}
explicit PointXZ(Residue u) : X(u), Z(u.ring_of().one()) {
}
explicit PointXZ(Residue y, bool) : X(y.ring_of().one() + y), Z(y.ring_of().one() - y) {
}
PointXZ(const PointXZ& P) : X(P.X), Z(P.Z) {
}
PointXZ& operator=(const PointXZ& P) {
X = P.X;
Z = P.Z;
return *this;
}
Residue get_u() const {
return X * inverse(Z);
}
Residue get_v(bool sign_v = false) const;
bool is_infty() const {
return Z.is_zero();
}
Residue get_y() const {
return (X - Z) * inverse(X + Z);
}
bool export_point_y(unsigned char buffer[32]) const;
bool export_point_u(unsigned char buffer[32]) const;
void zeroize() {
X = Z = Z.ring_of().zero();
}
};
PointXZ power_gen_xz(const Bignum& n) const;
PointXZ power_xz(const Residue& u, const Bignum& n) const;
PointXZ power_xz(const PointXZ& P, const Bignum& n) const;
PointXZ add_xz(const PointXZ& P, const PointXZ& Q) const;
PointXZ double_xz(const PointXZ& P) const;
PointXZ import_point_u(const unsigned char point[32]) const;
PointXZ import_point_y(const unsigned char point[32]) const;
};
const MontgomeryCurve& Curve25519();
} // namespace ellcurve

View file

@ -0,0 +1,255 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "ellcurve/TwEdwards.h"
#include <assert.h>
#include <cstring>
namespace ellcurve {
using namespace arith;
class TwEdwardsCurve;
TwEdwardsCurve::TwEdwardsCurve(const Residue& _D, const Residue& _Gy, td::Ref<ResidueRing> _R)
: ring(_R)
, D(_D)
, D2(_D + _D)
, Gy(_Gy)
, P_(_R->get_modulus())
, cofactor_short(0)
, G(_R)
, O(_R)
, table_lines(0)
, table() {
init();
}
TwEdwardsCurve::~TwEdwardsCurve() {
}
void TwEdwardsCurve::init() {
assert(D != ring->zero() && D != ring->convert(-1));
O.X = O.Z = ring->one();
G = SegrePoint(*this, Gy, 0);
assert(!G.XY.is_zero());
}
void TwEdwardsCurve::set_order_cofactor(const Bignum& order, int cof) {
assert(order > 0);
assert(cof >= 0);
assert(cof == 0 || (order % cof) == 0);
Order = order;
cofactor = cofactor_short = cof;
if (cof > 0) {
L = order / cof;
assert(is_prime(L));
assert(!power_gen(1).is_zero());
assert(power_gen(L).is_zero());
}
}
TwEdwardsCurve::SegrePoint::SegrePoint(const TwEdwardsCurve& E, const Residue& y, bool x_sign)
: XY(y), X(E.get_base_ring()), Y(y), Z(E.get_base_ring()->one()) {
Residue x(y.ring_ref());
if (E.recover_x(x, y, x_sign)) {
XY *= x;
X = x;
} else {
XY = Y = Z = E.get_base_ring()->zero();
}
}
bool TwEdwardsCurve::recover_x(Residue& x, const Residue& y, bool x_sign) const {
// recovers x from equation -x^2+y^2 = 1+d*x^2*y^2
Residue z = inverse(ring->one() + D * sqr(y));
if (z.is_zero()) {
return false;
}
z *= sqr(y) - ring->one();
Residue t = sqrt(z);
if (sqr(t) == z) {
x = (t.extract().odd() == x_sign) ? t : -t;
//std::cout << "x=" << x << ", y=" << y << std::endl;
return true;
} else {
return false;
}
}
void TwEdwardsCurve::add_points(SegrePoint& Res, const SegrePoint& P, const SegrePoint& Q) const {
Residue a((P.X + P.Y) * (Q.X + Q.Y));
Residue b((P.X - P.Y) * (Q.X - Q.Y));
Residue c(P.Z * Q.Z * ring->convert(2));
Residue d(P.XY * Q.XY * D2);
Residue x_num(a - b); // 2(x1y2+x2y1)
Residue y_num(a + b); // 2(x1x2+y1y2)
Residue x_den(c + d); // 2(1+dx1x2y1y2)
Residue y_den(c - d); // 2(1-dx1x2y1y2)
Res.X = x_num * y_den; // x = x_num/x_den, y = y_num/y_den
Res.Y = y_num * x_den;
Res.XY = x_num * y_num;
Res.Z = x_den * y_den;
}
TwEdwardsCurve::SegrePoint TwEdwardsCurve::add_points(const SegrePoint& P, const SegrePoint& Q) const {
SegrePoint Res(ring);
add_points(Res, P, Q);
return Res;
}
void TwEdwardsCurve::double_point(SegrePoint& Res, const SegrePoint& P) const {
add_points(Res, P, P);
}
TwEdwardsCurve::SegrePoint TwEdwardsCurve::double_point(const SegrePoint& P) const {
SegrePoint Res(ring);
double_point(Res, P);
return Res;
}
// computes u([n]P) in form (xy,x,y,1)*Z
TwEdwardsCurve::SegrePoint TwEdwardsCurve::power_point(const SegrePoint& A, const Bignum& n, bool uniform) const {
assert(n >= 0);
if (n == 0) {
return O;
}
int k = n.num_bits();
SegrePoint P(A);
if (uniform) {
SegrePoint Q(double_point(A));
for (int i = k - 2; i >= 0; --i) {
if (n[i]) {
add_points(P, P, Q);
double_point(Q, Q);
} else {
// we do more operations than necessary for uniformicity
add_points(Q, P, Q);
double_point(P, P);
}
}
} else {
for (int i = k - 2; i >= 0; --i) {
double_point(P, P);
if (n[i]) {
add_points(P, P, A); // may optimize further if A.z = 1
}
}
}
return P;
}
int TwEdwardsCurve::build_table() {
if (table.size()) {
return -1;
}
table_lines = (P_.num_bits() >> 2) + 2;
table.reserve(table_lines * 15 + 1);
table.emplace_back(get_base_point());
for (int i = 0; i < table_lines; i++) {
for (int j = 0; j < 15; j++) {
table.emplace_back(add_points(table[15 * i + j], table[15 * i]));
}
}
return 1;
}
int get_nibble(const Bignum& n, int idx) {
return n[idx * 4 + 3] * 8 + n[idx * 4 + 2] * 4 + n[idx * 4 + 1] * 2 + n[idx * 4];
}
TwEdwardsCurve::SegrePoint TwEdwardsCurve::power_gen(const Bignum& n, bool uniform) const {
if (uniform || n.num_bits() > table_lines * 4) {
return power_point(G, n, uniform);
} else if (n.is_zero()) {
return O;
} else {
int k = (n.num_bits() + 3) >> 2;
assert(k > 0 && k <= table_lines);
int x = get_nibble(n, k - 1);
assert(x > 0 && x < 16);
SegrePoint P(table[15 * (k - 1) + x - 1]);
for (int i = k - 2; i >= 0; i--) {
x = get_nibble(n, i);
assert(x >= 0 && x < 16);
if (x > 0) {
add_points(P, P, table[15 * i + x - 1]);
}
}
return P;
}
}
bool TwEdwardsCurve::SegrePoint::export_point(unsigned char buffer[32], bool need_x) const {
if (!is_normalized()) {
if (Z.is_zero()) {
std::memset(buffer, 0xff, 32);
return false;
}
Residue f(inverse(Z));
Bignum y((Y * f).extract());
assert(!y[255]);
if (need_x) {
y[255] = (X * f).extract().odd();
}
y.export_lsb(buffer, 32);
} else {
Bignum y(Y.extract());
assert(!y[255]);
if (need_x) {
y[255] = X.extract().odd();
}
y.export_lsb(buffer, 32);
}
return true;
}
bool TwEdwardsCurve::SegrePoint::export_point_u(unsigned char buffer[32]) const {
if (Z == Y) {
std::memset(buffer, 0xff, 32);
return false;
}
Residue f(inverse(Z - Y));
((Z + Y) * f).extract().export_lsb(buffer, 32);
assert(!(buffer[31] & 0x80));
return true;
}
TwEdwardsCurve::SegrePoint TwEdwardsCurve::import_point(const unsigned char point[32], bool& ok) const {
Bignum y;
y.import_lsb(point, 32);
bool x_sign = y[255];
y[255] = 0;
Residue yr(y, ring);
Residue xr(ring);
ok = recover_x(xr, yr, x_sign);
return ok ? SegrePoint(xr, yr) : SegrePoint(ring);
}
const TwEdwardsCurve& Ed25519() {
static const TwEdwardsCurve Ed25519 = [] {
TwEdwardsCurve res(Fp25519()->frac(-121665, 121666), Fp25519()->frac(4, 5), Fp25519());
res.set_order_cofactor(hex_string{"80000000000000000000000000000000a6f7cef517bce6b2c09318d2e7ae9f68"}, 8);
res.build_table();
return res;
}();
return Ed25519;
}
} // namespace ellcurve

145
crypto/ellcurve/TwEdwards.h Normal file
View file

@ -0,0 +1,145 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <vector>
#include "common/refcnt.hpp"
#include "openssl/residue.h"
#include "ellcurve/Fp25519.h"
namespace ellcurve {
using namespace arith;
class TwEdwardsCurve {
public:
struct SegrePoint {
Residue XY, X, Y, Z; // if x=X/Z and y=Y/T, stores (xy,x,y,1)*Z*T
SegrePoint(td::Ref<ResidueRing> R) : XY(R), X(R), Y(R), Z(R) {
}
SegrePoint(const Residue& x, const Residue& y) : XY(x * y), X(x), Y(y), Z(y.ring_of().one()) {
}
SegrePoint(const TwEdwardsCurve& E, const Residue& y, bool x_sign);
SegrePoint(const SegrePoint& P) : XY(P.XY), X(P.X), Y(P.Y), Z(P.Z) {
}
SegrePoint& operator=(const SegrePoint& P) {
XY = P.XY;
X = P.X;
Y = P.Y;
Z = P.Z;
return *this;
}
bool is_zero() const {
return X.is_zero() && (Y == Z);
}
bool is_valid() const {
return (XY * Z == X * Y) && !(XY.is_zero() && X.is_zero() && Y.is_zero() && Z.is_zero());
}
bool is_finite() const {
return !Z.is_zero();
}
bool is_normalized() const {
return Z == Z.ring_of().one();
}
SegrePoint& normalize() {
auto f = inverse(Z);
XY *= f;
X *= f;
Y *= f;
Z = Z.ring_of().one();
return *this;
}
SegrePoint& zeroize() {
XY = X = Y = Z = Z.ring_of().zero();
return *this;
}
bool export_point(unsigned char buffer[32], bool need_x = true) const;
bool export_point_y(unsigned char buffer[32]) const {
return export_point(buffer, false);
}
bool export_point_u(unsigned char buffer[32]) const;
Residue get_y() const {
return Y * inverse(Z);
}
Residue get_x() const {
return X * inverse(Z);
}
Residue get_u() const {
return (Z + Y) * inverse(Z - Y);
}
void negate() {
XY.negate();
X.negate();
}
};
private:
td::Ref<ResidueRing> ring;
Residue D;
Residue D2;
Residue Gy;
Bignum P_;
Bignum L;
Bignum Order;
Bignum cofactor;
int cofactor_short;
SegrePoint G;
SegrePoint O;
int table_lines;
std::vector<SegrePoint> table;
void init();
public:
TwEdwardsCurve(const Residue& _D, const Residue& _Gy, td::Ref<ResidueRing> _R);
~TwEdwardsCurve();
const Residue& get_gen_y() const {
return Gy;
}
const Bignum& get_ell() const {
return L;
}
const Bignum& get_order() const {
return Order;
}
td::Ref<ResidueRing> get_base_ring() const {
return ring;
}
const Bignum& get_p() const {
return P_;
}
const SegrePoint& get_base_point() const {
return G;
}
void set_order_cofactor(const Bignum& order, int cof);
bool recover_x(Residue& x, const Residue& y, bool x_sign) const;
void add_points(SegrePoint& R, const SegrePoint& P, const SegrePoint& Q) const;
SegrePoint add_points(const SegrePoint& P, const SegrePoint& Q) const;
void double_point(SegrePoint& R, const SegrePoint& P) const;
SegrePoint double_point(const SegrePoint& P) const;
SegrePoint power_point(const SegrePoint& A, const Bignum& n, bool uniform = false) const;
SegrePoint power_gen(const Bignum& n, bool uniform = false) const;
int build_table();
SegrePoint import_point(const unsigned char point[32], bool& ok) const;
};
std::ostream& operator<<(std::ostream& os, const TwEdwardsCurve::SegrePoint& P);
const TwEdwardsCurve& Ed25519();
} // namespace ellcurve

200
crypto/fift/Dictionary.cpp Normal file
View file

@ -0,0 +1,200 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "Dictionary.h"
namespace fift {
//
// WordDef
//
void WordDef::run(IntCtx& ctx) const {
auto next = run_tail(ctx);
while (next.not_null()) {
next = next->run_tail(ctx);
}
}
//
// StackWord
//
Ref<WordDef> StackWord::run_tail(IntCtx& ctx) const {
f(ctx.stack);
return {};
}
//
// CtxWord
//
Ref<WordDef> CtxWord::run_tail(IntCtx& ctx) const {
f(ctx);
return {};
}
//
// CtxTailWord
//
Ref<WordDef> CtxTailWord::run_tail(IntCtx& ctx) const {
return f(ctx);
}
//
// WordList
//
WordList::WordList(std::vector<Ref<WordDef>>&& _list) : list(std::move(_list)) {
}
WordList::WordList(const std::vector<Ref<WordDef>>& _list) : list(_list) {
}
WordList& WordList::push_back(Ref<WordDef> word_def) {
list.push_back(std::move(word_def));
return *this;
}
WordList& WordList::push_back(WordDef& wd) {
list.emplace_back(&wd);
return *this;
}
Ref<WordDef> WordList::run_tail(IntCtx& ctx) const {
if (list.empty()) {
return {};
}
auto it = list.cbegin(), it2 = list.cend() - 1;
while (it < it2) {
(*it)->run(ctx);
++it;
}
return *it;
}
void WordList::close() {
list.shrink_to_fit();
}
WordList& WordList::append(const std::vector<Ref<WordDef>>& other) {
list.insert(list.end(), other.begin(), other.end());
return *this;
}
//
// WordRef
//
WordRef::WordRef(Ref<WordDef> _def, bool _act) : def(std::move(_def)), active(_act) {
}
WordRef::WordRef(StackWordFunc func) : def(Ref<StackWord>{true, std::move(func)}), active(false) {
}
WordRef::WordRef(CtxWordFunc func, bool _act) : def(Ref<CtxWord>{true, std::move(func)}), active(_act) {
}
WordRef::WordRef(CtxTailWordFunc func, bool _act) : def(Ref<CtxTailWord>{true, std::move(func)}), active(_act) {
}
Ref<WordDef> WordRef::get_def() const & {
return def;
}
Ref<WordDef> WordRef::get_def() && {
return std::move(def);
}
void WordRef::operator()(IntCtx& ctx) const {
def->run(ctx);
}
bool WordRef::is_active() const {
return active;
}
//
// Dictionary
//
WordRef* Dictionary::lookup(td::Slice name) {
auto it = words_.find(name);
if (it == words_.end()) {
return nullptr;
}
return &it->second;
}
void Dictionary::def_ctx_word(std::string name, CtxWordFunc func) {
def_word(std::move(name), std::move(func));
}
void Dictionary::def_active_word(std::string name, CtxWordFunc func) {
Ref<WordDef> wdef = Ref<CtxWord>{true, std::move(func)};
def_word(std::move(name), {std::move(wdef), true});
}
void Dictionary::def_stack_word(std::string name, StackWordFunc func) {
def_word(std::move(name), std::move(func));
}
void Dictionary::def_ctx_tail_word(std::string name, CtxTailWordFunc func) {
def_word(std::move(name), std::move(func));
}
void Dictionary::def_word(std::string name, WordRef word) {
auto res = words_.emplace(name, std::move(word));
LOG_IF(FATAL, !res.second) << "Cannot redefine word: " << name;
}
void Dictionary::undef_word(td::Slice name) {
auto it = words_.find(name);
if (it == words_.end()) {
return;
}
words_.erase(it);
}
void interpret_nop(vm::Stack& stack) {
}
Ref<WordDef> Dictionary::nop_word_def = Ref<StackWord>{true, interpret_nop};
//
// functions for wordef
//
Ref<WordDef> pop_exec_token(vm::Stack& stack) {
stack.check_underflow(1);
auto wd_ref = stack.pop().as_object<WordDef>();
if (wd_ref.is_null()) {
throw IntError{"execution token expected"};
}
return wd_ref;
}
Ref<WordList> pop_word_list(vm::Stack& stack) {
stack.check_underflow(1);
auto wl_ref = stack.pop().as_object<WordList>();
if (wl_ref.is_null()) {
throw IntError{"word list expected"};
}
return wl_ref;
}
void push_argcount(vm::Stack& stack, int args) {
stack.push_smallint(args);
stack.push({vm::from_object, Dictionary::nop_word_def});
}
} // namespace fift

182
crypto/fift/Dictionary.h Normal file
View file

@ -0,0 +1,182 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <functional>
#include <map>
#include "IntCtx.h"
namespace fift {
using td::Ref;
/*
*
* WORD CLASSES
*
*/
typedef std::function<void(vm::Stack&)> StackWordFunc;
typedef std::function<void(IntCtx&)> CtxWordFunc;
class WordDef : public td::CntObject {
public:
WordDef() = default;
virtual ~WordDef() override = default;
virtual Ref<WordDef> run_tail(IntCtx& ctx) const = 0;
void run(IntCtx& ctx) const;
virtual bool is_list() const {
return false;
}
virtual long long list_size() const {
return -1;
}
virtual const std::vector<Ref<WordDef>>* get_list() const {
return nullptr;
}
};
class StackWord : public WordDef {
StackWordFunc f;
public:
StackWord(StackWordFunc _f) : f(std::move(_f)) {
}
~StackWord() override = default;
Ref<WordDef> run_tail(IntCtx& ctx) const override;
};
class CtxWord : public WordDef {
CtxWordFunc f;
public:
CtxWord(CtxWordFunc _f) : f(std::move(_f)) {
}
~CtxWord() override = default;
Ref<WordDef> run_tail(IntCtx& ctx) const override;
};
typedef std::function<Ref<WordDef>(IntCtx&)> CtxTailWordFunc;
class CtxTailWord : public WordDef {
CtxTailWordFunc f;
public:
CtxTailWord(CtxTailWordFunc _f) : f(std::move(_f)) {
}
~CtxTailWord() override = default;
Ref<WordDef> run_tail(IntCtx& ctx) const override;
};
class WordList : public WordDef {
std::vector<Ref<WordDef>> list;
public:
~WordList() override = default;
WordList() = default;
WordList(std::vector<Ref<WordDef>>&& _list);
WordList(const std::vector<Ref<WordDef>>& _list);
WordList& push_back(Ref<WordDef> word_def);
WordList& push_back(WordDef& wd);
Ref<WordDef> run_tail(IntCtx& ctx) const override;
void close();
bool is_list() const override {
return true;
}
long long list_size() const override {
return (long long)list.size();
}
const std::vector<Ref<WordDef>>* get_list() const override {
return &list;
}
WordList& append(const std::vector<Ref<WordDef>>& other);
WordList* make_copy() const override {
return new WordList(list);
}
};
class WordRef {
Ref<WordDef> def;
bool active;
public:
WordRef() = delete;
WordRef(const WordRef& ref) = default;
WordRef(WordRef&& ref) = default;
WordRef(Ref<WordDef> _def, bool _act = false);
WordRef(StackWordFunc func);
WordRef(CtxWordFunc func, bool _act = false);
WordRef(CtxTailWordFunc func, bool _act = false);
//WordRef(const std::vector<Ref<WordDef>>& word_list);
//WordRef(std::vector<Ref<WordDef>>&& word_list);
WordRef& operator=(const WordRef&) = default;
WordRef& operator=(WordRef&&) = default;
Ref<WordDef> get_def() const &;
Ref<WordDef> get_def() &&;
void operator()(IntCtx& ctx) const;
bool is_active() const;
~WordRef() = default;
};
/*
WordRef::WordRef(const std::vector<Ref<WordDef>>& word_list) : def(Ref<WordList>{true, word_list}) {
}
WordRef::WordRef(std::vector<Ref<WordDef>>&& word_list) : def(Ref<WordList>{true, std::move(word_list)}) {
}
*/
/*
*
* DICTIONARIES
*
*/
class Dictionary {
public:
WordRef* lookup(td::Slice name);
void def_ctx_word(std::string name, CtxWordFunc func);
void def_ctx_tail_word(std::string name, CtxTailWordFunc func);
void def_active_word(std::string name, CtxWordFunc func);
void def_stack_word(std::string name, StackWordFunc func);
void def_word(std::string name, WordRef word);
void undef_word(td::Slice name);
auto begin() const {
return words_.begin();
}
auto end() const {
return words_.end();
}
static Ref<WordDef> nop_word_def;
private:
std::map<std::string, WordRef, std::less<>> words_;
};
/*
*
* AUX FUNCTIONS FOR WORD DEFS
*
*/
Ref<WordDef> pop_exec_token(vm::Stack& stack);
Ref<WordList> pop_word_list(vm::Stack& stack);
void push_argcount(vm::Stack& stack, int args);
} // namespace fift

76
crypto/fift/Fift.cpp Normal file
View file

@ -0,0 +1,76 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "Fift.h"
#include "words.h"
#include "td/utils/PathView.h"
namespace fift {
Fift::Fift(Config config) : config_(std::move(config)) {
}
Fift::Config& Fift::config() {
return config_;
}
td::Result<int> Fift::interpret_file(std::string fname, std::string current_dir, bool is_interactive) {
auto r_file = config_.source_lookup.lookup_source(fname, current_dir);
if (r_file.is_error()) {
return td::Status::Error("cannot locate file `" + fname + "`");
}
auto file = r_file.move_as_ok();
IntCtx ctx;
std::stringstream ss(file.data);
ctx.input_stream = &ss;
ctx.filename = td::PathView(file.path).file_name().str();
ctx.currentd_dir = td::PathView(file.path).parent_dir().str();
ctx.include_depth = is_interactive ? 0 : 1;
return do_interpret(ctx);
}
td::Result<int> Fift::interpret_istream(std::istream& stream, std::string current_dir, bool is_interactive) {
IntCtx ctx;
ctx.input_stream = &stream;
ctx.filename = "stdin";
ctx.currentd_dir = current_dir;
ctx.include_depth = is_interactive ? 0 : 1;
return do_interpret(ctx);
}
td::Result<int> Fift::do_interpret(IntCtx& ctx) {
ctx.ton_db = &config_.ton_db;
ctx.source_lookup = &config_.source_lookup;
ctx.dictionary = &config_.dictionary;
ctx.output_stream = config_.output_stream;
ctx.error_stream = config_.error_stream;
if (!ctx.output_stream) {
return td::Status::Error("Cannot run interpreter without output_stream");
}
try {
return funny_interpret_loop(ctx);
} catch (fift::IntError ab) {
return td::Status::Error(ab.msg);
} catch (fift::Quit q) {
return q.res;
}
return 0;
}
} // namespace fift

53
crypto/fift/Fift.h Normal file
View file

@ -0,0 +1,53 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "SourceLookup.h"
#include "vm/db/TonDb.h"
#include "Dictionary.h"
#include "td/utils/Status.h"
namespace fift {
struct IntCtx;
int funny_interpret_loop(IntCtx& ctx);
struct Fift {
public:
struct Config {
fift::SourceLookup source_lookup;
vm::TonDb ton_db;
fift::Dictionary dictionary;
std::ostream* output_stream{&std::cout};
std::ostream* error_stream{&std::cerr};
};
// Fift must own ton_db and dictionary, no concurrent access is allowed
explicit Fift(Config config);
td::Result<int> interpret_file(std::string fname, std::string current_dir, bool interactive = false);
td::Result<int> interpret_istream(std::istream& stream, std::string current_dir, bool interactive = true);
Config& config();
private:
Config config_;
td::Result<int> do_interpret(IntCtx& ctx);
};
} // namespace fift

192
crypto/fift/IntCtx.cpp Normal file
View file

@ -0,0 +1,192 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "IntCtx.h"
namespace fift {
td::StringBuilder& operator<<(td::StringBuilder& os, const IntCtx& ctx) {
if (ctx.include_depth) {
return os << ctx.filename << ":" << ctx.line_no << ": ";
} else {
return os;
}
}
std::ostream& operator<<(std::ostream& os, const IntCtx& ctx) {
return os << (PSLICE() << ctx).c_str();
}
void CharClassifier::import_from_string(td::Slice str, int space_cls) {
set_char_class(' ', space_cls);
set_char_class('\t', space_cls);
int cls = 3;
for (char c : str) {
if (c == ' ') {
cls--;
} else {
set_char_class(c, cls);
}
}
}
void CharClassifier::import_from_string(std::string str, int space_cls) {
import_from_string(td::Slice{str}, space_cls);
}
void CharClassifier::import_from_string(const char* str, int space_cls) {
import_from_string(td::Slice{str}, space_cls);
}
CharClassifier CharClassifier::from_string(td::Slice str, int space_cls) {
return CharClassifier{str, space_cls};
}
void CharClassifier::set_char_class(int c, int cl) {
c &= 0xff;
cl &= 3;
int offs = (c & 3) * 2;
int mask = (3 << offs);
cl <<= offs;
unsigned char* p = data_ + (c >> 2);
*p = static_cast<unsigned char>((*p & ~mask) | cl);
}
IntCtx::Savepoint::Savepoint(IntCtx& _ctx, std::string new_filename, std::string new_current_dir,
std::istream* new_input_stream)
: ctx(_ctx)
, old_line_no(_ctx.line_no)
, old_filename(_ctx.filename)
, old_current_dir(_ctx.currentd_dir)
, old_input_stream(_ctx.input_stream)
, old_curline(_ctx.str)
, old_curpos(_ctx.input_ptr - _ctx.str.c_str()) {
ctx.line_no = 0;
ctx.filename = new_filename;
ctx.currentd_dir = new_current_dir;
ctx.input_stream = new_input_stream;
ctx.str = "";
ctx.input_ptr = 0;
++(ctx.include_depth);
}
IntCtx::Savepoint::~Savepoint() {
ctx.line_no = old_line_no;
ctx.filename = old_filename;
ctx.currentd_dir = old_current_dir;
ctx.input_stream = old_input_stream;
ctx.str = old_curline;
ctx.input_ptr = ctx.str.c_str() + old_curpos;
--(ctx.include_depth);
}
bool IntCtx::load_next_line() {
if (!std::getline(*input_stream, str)) {
return false;
}
if (!str.empty() && str.back() == '\r') {
str.pop_back();
}
set_input(str);
return true;
}
bool IntCtx::is_sb() const {
return !eof() && line_no == 1 && *input_ptr == '#' && input_ptr[1] == '!';
}
td::Slice IntCtx::scan_word_to(char delim, bool err_endl) {
auto ptr = input_ptr;
while (*ptr && *ptr != delim) {
ptr++;
}
if (*ptr) {
std::swap(ptr, input_ptr);
return td::Slice{ptr, input_ptr++};
} else if (err_endl && delim) {
throw IntError{std::string{"end delimiter `"} + delim + "` not found"};
} else {
std::swap(ptr, input_ptr);
return td::Slice{ptr, input_ptr};
}
}
td::Slice IntCtx::scan_word() {
skipspc(true);
auto ptr = input_ptr;
while (*ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '\r') {
ptr++;
}
auto ptr2 = ptr;
std::swap(ptr, input_ptr);
skipspc();
return td::Slice{ptr, ptr2};
}
td::Slice IntCtx::scan_word_ext(const CharClassifier& classifier) {
skipspc(true);
auto ptr = input_ptr;
while (*ptr && *ptr != '\r' && *ptr != '\n') {
int c = classifier.classify(*ptr);
if ((c & 1) && ptr != input_ptr) {
break;
}
ptr++;
if (c & 2) {
break;
}
}
std::swap(ptr, input_ptr);
return td::Slice{ptr, input_ptr};
}
void IntCtx::skipspc(bool skip_eol) {
do {
while (*input_ptr == ' ' || *input_ptr == '\t' || *input_ptr == '\r') {
++input_ptr;
}
if (!skip_eol || *input_ptr) {
break;
}
} while (load_next_line());
}
void check_compile(const IntCtx& ctx) {
if (ctx.state <= 0) {
throw IntError{"compilation mode only"};
}
}
void check_execute(const IntCtx& ctx) {
if (ctx.state != 0) {
throw IntError{"interpret mode only"};
}
}
void check_not_int_exec(const IntCtx& ctx) {
if (ctx.state < 0) {
throw IntError{"not allowed in internal interpret mode"};
}
}
void check_int_exec(const IntCtx& ctx) {
if (ctx.state >= 0) {
throw IntError{"internal interpret mode only"};
}
}
} // namespace fift

147
crypto/fift/IntCtx.h Normal file
View file

@ -0,0 +1,147 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "crypto/vm/db/TonDb.h" // FIXME
#include "crypto/vm/stack.hpp"
#include "crypto/common/bitstring.h"
#include <cstdint>
#include <cstring>
#include <iostream>
#include <string>
namespace fift {
class Dictionary;
class SourceLookup;
struct IntError {
std::string msg;
IntError(std::string _msg) : msg(_msg) {
}
};
class CharClassifier {
unsigned char data_[64];
public:
CharClassifier() {
std::memset(data_, 0, sizeof(data_));
}
CharClassifier(td::Slice str, int space_cls = 3) : CharClassifier() {
import_from_string(str, space_cls);
}
CharClassifier(std::string str, int space_cls = 3) : CharClassifier(td::Slice{str}, space_cls) {
}
CharClassifier(const char* str, int space_cls = 3) : CharClassifier(td::Slice{str}, space_cls) {
}
void import_from_string(td::Slice str, int space_cls = 3);
void import_from_string(std::string str, int space_cls = 3);
void import_from_string(const char* str, int space_cls = 3);
static CharClassifier from_string(td::Slice str, int space_cls = 3);
void set_char_class(int c, int cl);
int classify(int c) const {
c &= 0xff;
int offs = (c & 3) * 2;
return (data_[(unsigned)c >> 2] >> offs) & 3;
}
};
struct IntCtx {
vm::Stack stack;
int state{0};
int include_depth{0};
int line_no{0};
std::string filename;
std::string currentd_dir;
std::istream* input_stream{nullptr};
std::ostream* output_stream{nullptr};
std::ostream* error_stream{nullptr};
vm::TonDb* ton_db{nullptr};
Dictionary* dictionary{nullptr};
SourceLookup* source_lookup{nullptr};
private:
std::string str;
const char* input_ptr;
public:
IntCtx() = default;
operator vm::Stack&() {
return stack;
}
td::Slice scan_word_to(char delim, bool err_endl = true);
td::Slice scan_word();
td::Slice scan_word_ext(const CharClassifier& classifier);
void skipspc(bool skip_eol = false);
bool eof() const {
return !*input_stream;
}
bool not_eof() const {
return !eof();
}
void set_input(std::string input_str) {
str = input_str;
input_ptr = str.c_str();
++line_no;
}
void set_input(const char* ptr) {
input_ptr = ptr;
}
const char* get_input() const {
return input_ptr;
}
bool load_next_line();
bool is_sb() const;
void clear() {
state = 0;
stack.clear();
}
class Savepoint {
IntCtx& ctx;
int old_line_no;
std::string old_filename;
std::string old_current_dir;
std::istream* old_input_stream;
std::string old_curline;
std::ptrdiff_t old_curpos;
public:
Savepoint(IntCtx& _ctx, std::string new_filename, std::string new_current_dir, std::istream* new_input_stream);
~Savepoint();
};
};
void check_compile(const IntCtx& ctx);
void check_execute(const IntCtx& ctx);
void check_not_int_exec(const IntCtx& ctx);
void check_int_exec(const IntCtx& ctx);
td::StringBuilder& operator<<(td::StringBuilder& os, const IntCtx& ctx);
std::ostream& operator<<(std::ostream& os, const IntCtx& ctx);
} // namespace fift

View file

@ -0,0 +1,89 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "SourceLookup.h"
#include "td/utils/PathView.h"
#include "td/utils/PathView.h"
#include "td/utils/port/path.h"
#include "td/utils/filesystem.h"
#include <fstream>
namespace fift {
td::Result<FileLoader::File> OsFileLoader::read_file(td::CSlice filename) {
File res;
TRY_RESULT(data, td::read_file_str(filename));
res.data = std::move(data);
TRY_RESULT(path, td::realpath(filename));
res.path = std::move(path);
return std::move(res);
}
td::Status OsFileLoader::write_file(td::CSlice filename, td::Slice data) {
return td::write_file(filename, data);
}
td::Result<FileLoader::File> OsFileLoader::read_file_part(td::CSlice filename, td::int64 size, td::int64 offset) {
File res;
TRY_RESULT(data, td::read_file_str(filename, size, offset));
res.data = std::move(data);
TRY_RESULT(path, td::realpath(filename));
res.path = std::move(path);
return std::move(res);
}
bool OsFileLoader::is_file_exists(td::CSlice filename) {
return td::stat(filename).is_ok();
}
void SourceLookup::add_include_path(td::string path) {
if (path.empty()) {
return;
}
if (!td::PathView(path).is_dir()) {
path += TD_DIR_SLASH;
}
source_include_path_.push_back(std::move(path));
}
td::Result<FileLoader::File> SourceLookup::lookup_source(std::string filename, std::string current_dir) {
CHECK(file_loader_);
if (!current_dir.empty() && !td::PathView(current_dir).is_dir()) {
current_dir += TD_DIR_SLASH;
}
if (td::PathView(filename).is_absolute()) {
return read_file(filename);
}
if (!current_dir.empty()) {
auto res = read_file(current_dir + filename);
if (res.is_ok()) {
return res;
}
}
for (auto& dir : source_include_path_) {
auto res = read_file(dir + filename);
if (res.is_ok()) {
return res;
}
}
return td::Status::Error(PSLICE() << "failed to lookup file: " << filename);
}
} // namespace fift

View file

@ -0,0 +1,71 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <iostream>
#include "td/utils/Status.h"
namespace fift {
class FileLoader {
public:
virtual ~FileLoader() = default;
struct File {
std::string data;
std::string path;
};
virtual td::Result<File> read_file(td::CSlice filename) = 0;
virtual td::Status write_file(td::CSlice filename, td::Slice data) = 0;
virtual td::Result<File> read_file_part(td::CSlice filename, td::int64 size, td::int64 offset) = 0;
virtual bool is_file_exists(td::CSlice filename) = 0;
};
class OsFileLoader : public FileLoader {
public:
td::Result<File> read_file(td::CSlice filename) override;
td::Status write_file(td::CSlice filename, td::Slice data) override;
td::Result<File> read_file_part(td::CSlice filename, td::int64 size, td::int64 offset) override;
bool is_file_exists(td::CSlice filename) override;
};
class SourceLookup {
public:
SourceLookup() = default;
explicit SourceLookup(std::unique_ptr<FileLoader> file_loader) : file_loader_(std::move(file_loader)) {
}
void add_include_path(td::string path);
td::Result<FileLoader::File> lookup_source(std::string filename, std::string current_dir);
td::Result<FileLoader::File> read_file(td::CSlice path) {
return file_loader_->read_file(path);
}
td::Status write_file(td::CSlice path, td::Slice data) {
return file_loader_->write_file(path, data);
}
td::Result<FileLoader::File> read_file_part(td::CSlice filename, td::int64 size, td::int64 offset) {
return file_loader_->read_file_part(filename, size, offset);
}
bool is_file_exists(td::CSlice filename) {
return file_loader_->is_file_exists(filename);
}
protected:
std::unique_ptr<FileLoader> file_loader_;
std::vector<std::string> source_include_path_;
};
} // namespace fift

221
crypto/fift/fift-main.cpp Normal file
View file

@ -0,0 +1,221 @@
/*
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-2019 Telegram Systems LLP
*/
#include "vm/stack.hpp"
#include <cassert>
#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include <fstream>
#include <memory>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <functional>
#include <getopt.h>
#include "Fift.h"
#include "Dictionary.h"
#include "SourceLookup.h"
#include "words.h"
#include "vm/db/TonDb.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Parser.h"
#include "td/utils/port/path.h"
void usage(const char* progname) {
std::cerr << "A simple Fift interpreter. Type `bye` to quit, or `words` to get a list of all commands\n";
std::cerr
<< "usage: " << progname
<< " [-i] [-n] [-I <source-include-path>] {-L <library-fif-file>} <source-file1-fif> <source-file2-fif> ...\n";
std::cerr << "\t-n\tDo not preload standard preamble file `Fift.fif`\n"
"\t-i\tForce interactive mode even if explicit source file names are indicated\n"
"\t-I<source-search-path>\tSets colon-separated library source include path. If not indicated, "
"$FIFTPATH is used instead.\n"
"\t-L<library-fif-file>\tPre-loads a library source file\n"
"\t-d<ton-db-path>\tUse a ton database\n"
"\t-s\tScript mode: use first argument as a fift source file and import remaining arguments as $n)\n"
"\t-v<verbosity-level>\tSet verbosity level\n";
std::exit(2);
}
void parse_include_path_set(std::string include_path_set, std::vector<std::string>& res) {
td::Parser parser(include_path_set);
while (!parser.empty()) {
auto path = parser.read_till_nofail(':');
if (!path.empty()) {
res.push_back(path.str());
}
parser.skip_nofail(':');
}
}
int main(int argc, char* const argv[]) {
bool interactive = false;
bool fift_preload = true, no_env = false;
bool script_mode = false;
std::vector<std::string> library_source_files, source_list;
std::vector<std::string> source_include_path;
std::string ton_db_path;
fift::Fift::Config config;
int i;
int new_verbosity_level = VERBOSITY_NAME(INFO);
while (!script_mode && (i = getopt(argc, argv, "hinI:L:d:sv:")) != -1) {
switch (i) {
case 'i':
interactive = true;
break;
case 'n':
fift_preload = false;
break;
case 'I':
parse_include_path_set(optarg, source_include_path);
no_env = true;
break;
case 'L':
library_source_files.emplace_back(optarg);
break;
case 'd':
ton_db_path = optarg;
break;
case 's':
script_mode = true;
break;
case 'v':
new_verbosity_level = VERBOSITY_NAME(FATAL) + td::to_integer<int>(td::Slice(optarg));
break;
case 'h':
default:
usage(argv[0]);
}
}
SET_VERBOSITY_LEVEL(new_verbosity_level);
while (optind < argc) {
source_list.emplace_back(argv[optind++]);
if (script_mode) {
break;
}
}
if (!no_env) {
const char* path = std::getenv("FIFTPATH");
if (path) {
parse_include_path_set(path ? path : "/usr/lib/fift", source_include_path);
}
}
std::string current_dir;
auto r_current_dir = td::realpath(".");
if (r_current_dir.is_ok()) {
current_dir = r_current_dir.move_as_ok();
source_include_path.push_back(current_dir);
}
config.source_lookup = fift::SourceLookup(std::make_unique<fift::OsFileLoader>());
for (auto& path : source_include_path) {
config.source_lookup.add_include_path(path);
}
if (!ton_db_path.empty()) {
auto r_ton_db = vm::TonDbImpl::open(ton_db_path);
if (r_ton_db.is_error()) {
LOG(ERROR) << "Error opening ton database: " << r_ton_db.error().to_string();
std::exit(2);
}
config.ton_db = r_ton_db.move_as_ok();
// FIXME //std::atexit([&] { config.ton_db.reset(); });
}
fift::init_words_common(config.dictionary);
fift::init_words_vm(config.dictionary);
fift::init_words_ton(config.dictionary);
if (script_mode) {
fift::import_cmdline_args(config.dictionary, source_list.empty() ? "" : source_list[0], argc - optind,
argv + optind);
}
fift::Fift fift(std::move(config));
if (fift_preload) {
auto status = fift.interpret_file("Fift.fif", "");
if (status.is_error()) {
LOG(ERROR) << "Error interpreting standard preamble file `Fift.fif`: " << status.error().message()
<< "\nCheck that correct include path is set by -I or by FIFTPATH environment variable, or disable "
"standard preamble by -n.\n";
std::exit(2);
}
}
for (auto source : library_source_files) {
auto status = fift.interpret_file(source, "");
if (status.is_error()) {
LOG(ERROR) << "Error interpreting preloaded file `" << source << "`: " << status.error().message();
std::exit(2);
}
}
if (source_list.empty()) {
interactive = true;
}
for (const auto& source : source_list) {
bool is_stdin = (source.empty() || source == "-");
auto status =
!is_stdin ? fift.interpret_file(source, current_dir) : fift.interpret_istream(std::cin, current_dir, false);
if (status.is_error()) {
if (!is_stdin) {
LOG(ERROR) << "Error interpreting file `" << source << "`: " << status.error().message();
} else {
LOG(ERROR) << "Error interpreting stdin: " << status.error().message();
}
std::exit(2);
}
auto res = status.move_as_ok();
if (res) {
std::exit(~res);
}
}
if (interactive) {
auto status = fift.interpret_istream(std::cin, current_dir);
if (status.is_error()) {
LOG(ERROR) << status.error().message();
std::exit(2);
} else {
int res = status.move_as_ok();
if (res) {
std::exit(~res);
}
}
}
}

1101
crypto/fift/lib/Asm.fif Normal file

File diff suppressed because it is too large Load diff

83
crypto/fift/lib/Fift.fif Normal file
View file

@ -0,0 +1,83 @@
{ 0 word drop 0 'nop } :: //
{ char " word 1 { swap { abort } if drop } } ::_ abort"
{ { bl word dup "" $= abort"comment extends after end of file" "*/" $= } until 0 'nop } :: /*
// { bl word 1 2 ' (create) } "::" 1 (create)
// { bl word 0 2 ' (create) } :: :
// { bl word 2 2 ' (create) } :: :_
// { bl word 3 2 ' (create) } :: ::_
// { bl word 0 (create) } : create
// { bl word (forget) } : forget
{ bl word 1 ' (forget) } :: [forget]
{ char " word 1 ' type } ::_ ."
{ swap ({) over 2+ -roll swap (compile) (}) } : does
{ 1 'nop does create } : constant
{ 2 'nop does create } : 2constant
{ hole constant } : variable
10 constant ten
{ bl word 1 { find 0= abort"word not found" } } :: (')
{ bl word find not abort"-?" 0 swap } :: [compile]
{ bl word 1 {
dup find { " -?" $+ abort } ifnot nip execute
} } :: @'
{ bl word 1 { swap 1 'nop does swap 0 (create) }
} :: =:
{ bl word 1 { -rot 2 'nop does swap 0 (create) }
} :: 2=:
{ <b swap s, b> } : s>c
{ s>c hash } : shash
// to be more efficiently re-implemented in C++ in the future
{ dup 0< ' negate if } : abs
{ 2dup > ' swap if } : minmax
{ minmax drop } : min
{ minmax nip } : max
"" constant <#
' $reverse : #>
{ swap 10 /mod char 0 + rot swap hold } : #
{ { # over 0<= } until } : #s
{ 0< { char - hold } if } : sign
// { dup abs <# #s rot sign #> nip } : (.)
// { (.) type } : ._
// { ._ space } : .
{ bl (-trailing) } : -trailing
{ char 0 (-trailing) } : -trailing0
{ char " word 1 ' $+ } ::_ +"
{ find 0<> dup ' nip if } : (def?)
{ bl word 1 ' (def?) } :: def?
{ bl word 1 { (def?) not } } :: undef?
{ def? ' skip-to-eof if } : skip-ifdef
{ bl word dup (def?) { drop skip-to-eof } { 'nop swap 0 (create) } cond } : library
{ bl word dup (def?) { 2drop skip-to-eof } { swap 1 'nop does swap 0 (create) } cond } : library-version
{ char ) word "$" swap $+ 1 { find 0= abort"undefined parameter" execute } } ::_ $(
// b s -- ?
{ sbitrefs rot brembitrefs rot >= -rot <= and } : s-fits?
{ 0 swap ! } : 0!
{ tuck @ + swap ! } : +!
{ tuck @ swap - swap ! } : -!
{ 1 swap +! } : 1+!
{ -1 swap +! } : 1-!
{ null swap ! } : null!
0 tuple constant nil
{ 1 tuple } : single
{ 2 tuple } : pair
{ 3 tuple } : triple
{ 1 untuple } : unsingle
{ 2 untuple } : unpair
{ 3 untuple } : untriple
{ over tuple? { swap count = } { 2drop false } cond } : tuple-len?
{ 0 tuple-len? } : nil?
{ 1 tuple-len? } : single?
{ 2 tuple-len? } : pair?
{ 3 tuple-len? } : triple?
{ 0 [] } : first
{ 1 [] } : second
{ 2 [] } : third
' pair : cons
' unpair : uncons
{ 0 [] } : car
{ 1 [] } : cdr
{ cdr car } : cadr
{ cdr cdr } : cddr
{ cdr cdr car } : caddr
{ null ' cons rot times } : list
{ true (atom) drop } : atom
{ bl word atom 1 'nop } ::_ `

436
crypto/fift/lib/Lisp.fif Normal file
View file

@ -0,0 +1,436 @@
library Lisp // tiny Lisp (or rather Scheme) interpreter
"Lists.fif" include
variable lisp-dict
{ hole dup 1 { @ execute } does create } : recursive
{ atom>$ +" undefined" abort } : report-not-found
// a l -- d -1 or a 0 Look up definition d of atom a in dictionary l
{ { dup null? { drop false true }
{ uncons -rot unpair -rot over eq?
{ drop nip true true } { nip swap false } cond
} cond
} until
} : lookup-in
// a dict -- def
{ lookup-in ' report-not-found ifnot } : lookup-or-fail
{ lisp-dict @ lookup-or-fail } : lisp-dict-lookup
// a d -- Defines a with definition d in dictionary lisp-dict
{ pair lisp-dict @ cons lisp-dict ! } : lisp-dict-int-define
{ box lisp-dict-int-define } : lisp-dict-define
// a d -- Defines new a with defininition d
{ over lisp-dict @ lookup-in { 2drop atom>$ +" already defined" abort }
{ drop lisp-dict-int-define } cond
} : lisp-dict-int-define-new
{ box lisp-dict-int-define-new } : lisp-dict-define-new
// a e -- Defines a with executable definition given by e
{ single lisp-dict-define-new } : lisp-dict-define-exec
// expr ctx def -- val
{ dup first execute } : run-definition
// expr ctx -- val
recursive lisp-ctx-eval {
over tuple?
{ over first over lisp-ctx-eval run-definition }
{ over atom? { lookup-or-fail @ } { drop } cond }
cond
} swap !
// exp -- value
{ lisp-dict @ lisp-ctx-eval } : lisp-eval
// (exprs) ctx -- (vals)
recursive lisp-ctx-eval-list
{ over null? { drop } {
swap uncons -rot over lisp-ctx-eval -rot lisp-ctx-eval-list cons
} cond
} swap !
// (exprs) ctx -- val
{ null rot {
dup null? { drop nip true } {
nip uncons swap 2 pick lisp-ctx-eval swap false
} cond } until
} : lisp-ctx-eval-list-last
// l c -- (args)
{ swap uncons nip swap lisp-ctx-eval-list } : extract-eval-arg-list
{ drop uncons nip } : extract-arg-list
// (x1 .. xn) e n -- x1 .. xn e
{ { swap uncons rot } swap times
swap null? not abort"invalid number of arguments"
} : unpack-list
// l c n e -- v
{ swap 2swap extract-eval-arg-list // e n (args)
-rot unpack-list execute
} : eval-exec-fixed
// l c n e -- v
{ 2 pick pair
swap 2swap extract-arg-list // [e c] n (args)
-rot unpack-list unpair swap execute
} : exec-fixed
// l c e -- v
{ -rot extract-eval-arg-list // e (args)
swap execute
} : eval-exec-list
{ -rot tuck extract-arg-list // e c (args)
swap rot execute
} : exec-list
// e a n --
{ rot 2 { // expr ctx def n e
rot drop eval-exec-fixed } does
lisp-dict-define-exec
} : lisp-fixed-primitive
{ rot 2 { rot drop exec-fixed } does lisp-dict-define-exec
} : lisp-fixed-lazy-primitive
// e a --
{ swap 1 { nip eval-exec-list } does lisp-dict-define-exec
} : lisp-primitive
{ swap 1 { nip exec-list } does lisp-dict-define-exec
} : lisp-lazy-primitive
// Uncomment next line for Fift booleans
// false constant #f true constant #t null constant no-answer
// Uncomment next line for Scheme booleans
`#f constant #f `#t constant #t #f constant no-answer
{ #f eq? } : lisp-false?
{ lisp-false? 0= } : lisp-true?
{ ' #t ' #f cond } : lisp-bool
// temp for defining a lot of primitives
{ bl word atom lisp-primitive } : L:
{ bl word atom swap lisp-dict-define } : L=:
{ bl word atom swap lisp-fixed-primitive } : #L:
{ 0 #L: } : 0L:
{ 1 #L: } : 1L:
{ 2 #L: } : 2L:
// basic primitives
{ sum-list } L: +
{ - } 2L: -
{ dup null? { drop 1 } { ' * foldl-ne } cond } L: *
{ / } 2L: /
{ mod } 2L: modulo
{ abs } 1L: abs
{ ' min foldl-ne } L: min
{ ' max foldl-ne } L: max
{ true ' and foldl } L: integer-and
{ false ' or foldl } L: integer-or
{ 0 ' xor foldl } L: integer-xor
{ not } 1L: integer-not
{ = lisp-bool } 2L: =
{ <> lisp-bool } 2L: <>
{ < lisp-bool } 2L: <
{ <= lisp-bool } 2L: <=
{ > lisp-bool } 2L: >
{ >= lisp-bool } 2L: >=
{ eq? lisp-bool } 2L: eq?
{ eqv? lisp-bool } 2L: eqv?
{ equal? lisp-bool } 2L: equal?
{ cons } 2L: cons
{ car } 1L: car
{ cdr } 1L: cdr
{ cadr } 1L: cadr
{ cddr } 1L: cddr
{ caddr } 1L: caddr
{ cdr cddr } 1L: cdddr
{ concat-list-lists } L: append
{ list-reverse } 1L: reverse
{ list-tail } 2L: list-tail
{ list-ref } 2L: list-ref
{ list-member-eq } 2L: memq
{ list-member-eqv } 2L: memv
{ list-member-equal } 2L: member
{ assq ' #f ifnot } 2L: assq
{ assv ' #f ifnot } 2L: assv
{ assoc ' #f ifnot } 2L: assoc
{ list? lisp-bool } 1L: list?
{ pair? lisp-bool } 1L: pair?
{ tuple? lisp-bool } 1L: tuple?
{ string? lisp-bool } 1L: string?
{ integer? lisp-bool } 1L: integer?
{ integer? lisp-bool } 1L: number?
{ count } 1L: width
{ list-length } 1L: length
{ [] } 2L: tuple-ref
{ first } 1L: first
{ second } 1L: second
{ third } 1L: third
{ 3 [] } 1L: fourth
{ list>tuple } 1L: list->tuple
{ explode list } 1L: tuple->list
null L=: null
{ atom? lisp-bool } 1L: symbol?
{ atom } 1L: string->symbol
{ atom>$ } 1L: symbol->string
{ dup #f eq? swap #t eq? or lisp-bool } 1L: boolean?
#t L=: else
#f L=: #f
#t L=: #t
{ null? lisp-bool } 1L: null?
{ 0= lisp-bool } 1L: zero?
{ 0> lisp-bool } 1L: positive?
{ 0< lisp-bool } 1L: negative?
{ 1 and 0= lisp-bool } 1L: even?
{ 1 and 0<> lisp-bool } 1L: odd?
{ bye } 0L: exit
{ .l null } 1L: write
{ lisp-eval } 1L: eval
{ drop } `quote 1 lisp-fixed-lazy-primitive
'nop L: list
{ list>tuple } L: tuple
{ list-last } L: begin
{ $len } 1L: string-length
{ concat-string-list } L: string-append
{ $= lisp-bool } 2L: string=?
{ $cmp 0< lisp-bool } 2L: string<?
{ $cmp 0<= lisp-bool } 2L: string<=?
{ $cmp 0> lisp-bool } 2L: string>?
{ $cmp 0>= lisp-bool } 2L: string>=?
{ (number) dup 1 = { drop } { ' 2drop if no-answer } cond
} 1L: string->number
{ (.) } 1L: number->string
{ box? lisp-bool } 1L: box?
{ box } 1L: box
{ hole } 0L: new-box
{ @ } 1L: unbox
{ tuck swap ! } 2L: set-box!
{ abort } 1L: error
{ dup find { nip execute } { +" -?" abort } cond } : find-execute
{ explode-list 1- roll find-execute } L: fift-exec
{ explode-list dup 1- swap roll find-execute } L: fift-exec-cnt
{ uncons swap find-execute } L: fift-exec-list
// end of basic primitives
forget L: forget #L: forget L=:
forget 0L: forget 1L: forget 2L:
{ { dup tuple? ' do-quote if } list-map } : map-quote
{ uncons ' cons foldr-ne map-quote
null swap cons lisp-dict @ rot run-definition
} `apply lisp-primitive // bad: should have preserved original context
// e1 e2 e3 ctx
{ 3 exch 3 pick lisp-ctx-eval lisp-true? ' swap if nip swap lisp-ctx-eval }
`if 3 lisp-fixed-lazy-primitive
// (e) ctx
{ #t -rot
{ over null? { 2drop true } {
swap uncons swap 2 pick lisp-ctx-eval dup lisp-true? // v' c t v ?
{ swap 2swap nip false } { -rot 2drop nip true } cond
} cond } until
} `and lisp-lazy-primitive
{ #f -rot
{ over null? { 2drop true } {
swap uncons swap 2 pick lisp-ctx-eval dup lisp-false? // v' c t v ?
{ swap 2swap nip false } { -rot 2drop nip true } cond
} cond } until
} `or lisp-lazy-primitive
{ lisp-false? lisp-bool } `not 1 lisp-fixed-primitive
// cond-clause ctx -- v -1 or 0
{ swap uncons -rot dup `else eq? {
drop lisp-ctx-eval-list-last true } {
over lisp-ctx-eval lisp-true? {
lisp-ctx-eval-list-last true } {
2drop false
} cond } cond
} : eval-cond-clause
// (clauses) ctx -- v
{ { over null? { no-answer true } {
swap uncons -rot over eval-cond-clause } cond
} until -rot 2drop
} `cond lisp-lazy-primitive
{ lisp-dict @ lookup-in { hole tuck lisp-dict-int-define } ifnot
} : lisp-create-global-var
// a e ctx -- old (simple) define
{ drop over atom? not abort"only a variable can be define'd"
over lisp-create-global-var swap lisp-eval swap !
} drop // `define 2 lisp-fixed-lazy-primitive
{ tuck lisp-ctx-eval rot dup atom? not abort"only a variable can be set"
rot lookup-or-fail dup @ -rot !
} `set! 2 lisp-fixed-lazy-primitive
// define lambda
{ { dup null? { drop true true }
{ uncons swap atom? { false } { drop false true } cond } cond
} until
} : var-list?
{ { dup null? over atom? or { drop true true }
{ uncons swap atom? { false } { drop false true } cond } cond
} until
} : lambda-var-list?
// (quote x) -- x -1 ; else 0
{ dup pair? { uncons swap `quote eq? { car true } { drop false } cond }
{ drop false } cond
} : is-quote?
recursive match-arg-list-acc
// l (vars) (args) -- ((var . arg) ...)+l -1 or ? 0
{ over atom? { over `_ eq? { 2drop } { pair swap cons } cond true } {
over null? { nip null? } { // (vars) (args)
over tuple? not { 2drop false } {
over is-quote? { eq? nip } { // (v) (a)
dup tuple? not { 2drop false } {
over count over count over <> { drop 2drop false } { // l [v] [a] n
3 roll 0 rot { // [v] [a] l i
dup 0< {
3 pick over [] swap // [v] [a] l vi i
3 pick over [] 2swap rot // [v] [a] i l vi ai
match-arg-list-acc { // [v] [a] i l'
swap 1+ } { nip -1 } cond
} ifnot
} swap times
2swap 2drop 0>=
} cond } cond } cond } cond } cond } cond
} swap !
{ null -rot match-arg-list-acc } : match-arg-list
// ((var . arg)...) ctx -- ctx'
{ { over null? not }
{ swap uncons swap unpair box pair rot cons } while
nip
} : extend-ctx-by-list
// ((vars) body) ctx
{ swap uncons -rot
dup lambda-var-list? not abort"invalid formal parameter list"
{ // l-expr ctx' [_ body ctx (vars)]
-rot 2 pick 3 [] swap rot // [_ body ...] (vars) ctx' l-expr
uncons nip swap lisp-ctx-eval-list // [_ body ...] (vars) (arg-vals)
match-arg-list not abort"invalid arguments to lambda" // [_ body ...] ((var arg)...)
over third extend-ctx-by-list // [_ body ctx (vars)] ctx''
swap second swap lisp-ctx-eval-list-last
} 3 -roll 4 tuple
} : make-lambda
{ make-lambda } `lambda lisp-lazy-primitive
// (a e) ctx -- more sophisticated (define a e)
{ drop uncons swap dup atom? { // (e) a
tuck lisp-create-global-var
swap lisp-dict @ lisp-ctx-eval-list-last swap !
} { // (e) (a v..)
uncons over atom? not abort"only variables can be define'd" // (e) a (v..)
rot cons over lisp-create-global-var // a ((v..) (e)) h
swap lisp-dict @ make-lambda swap !
} cond
} `define lisp-lazy-primitive
// ((x e) ..) ctx -- ((x.v) ..)
recursive eval-assign-list
{ over null? { drop } {
swap uncons swap uncons // ctx t x (e)
over atom? not abort"invalid variable name in assignment list"
3 pick lisp-ctx-eval-list-last // ctx t x v
pair swap rot eval-assign-list cons
} cond
} swap !
// (((x v) ..) body) ctx -- let construct
{ swap uncons swap 2 pick eval-assign-list // ctx body ((x v)...)
rot extend-ctx-by-list lisp-ctx-eval-list-last
} `let lisp-lazy-primitive
// ((x e) ..) ctx -- ctx'
{ swap {
dup null? { drop true } {
uncons swap uncons // ctx t x (e)
over atom? not abort"invalid variable name in assignment list"
3 pick lisp-ctx-eval-list-last // ctx t x v
box pair rot cons swap false
} cond } until
} : compute-let*-ctx
// (((x v) ..) body) ctx -- let* construct
{ swap uncons swap rot compute-let*-ctx lisp-ctx-eval-list-last
} `let* lisp-lazy-primitive
// ((x e) ..) ctx -- ((h e) ..) ctx' , with x bound to h in ctx'
recursive prepare-letrec-ctx {
over null? {
swap uncons swap uncons swap // ctx t (e) x
hole tuck pair swap rot cons // ctx t (x.h) (h e)
3 -roll rot cons prepare-letrec-ctx // (h e) t ctx'
-rot cons swap
} ifnot
} swap !
// (((x v) ..) body) ctx -- letrec construct
{ swap uncons swap rot prepare-letrec-ctx swap { // body ctx' ((h e)..)
dup null? { drop true } {
uncons -rot uncons 2 pick lisp-ctx-eval-list-last // body t ctx' h v
swap ! swap false
} cond } until
lisp-ctx-eval-list-last
} `letrec lisp-lazy-primitive
// (e (p e)...) ctx -- match construct
{ swap uncons swap 2 pick lisp-ctx-eval swap { // ctx v ((p e)..)
dup null? { drop 2drop no-answer true } {
uncons swap uncons swap 3 pick // ctx v t e p v
match-arg-list { // ctx v t e ((x' . v')...)
2swap 2drop rot extend-ctx-by-list lisp-ctx-eval-list-last true } {
2drop false
} cond } cond } until
} `match lisp-lazy-primitive
//
lisp-dict @ constant original-lisp-dict
{ original-lisp-dict lisp-dict ! } : reset-lisp
{ ' drop { lisp-eval .l cr } List-generic( } :_ LISP-EVAL-PRINT(
// LISP-EVAL-PRINT((+ 3 4) (* 5 6)) computes and prints 12 and 30
{ hole dup 1 { @ nip } does swap
1 { swap lisp-eval swap ! } does
List-generic(
} :_ LISP-EVAL(
// LISP-EVAL((+ 3 4) (* 5 6)) computes 12 and 30, returns only 30
// /*
LISP-EVAL-PRINT(
(define succ (lambda (x) (+ x 1)))
(define (twice f) (lambda (x) (f (f x))))
(define (fact n) (if (= n 0) 1 (* n (fact (- n 1)))))
(fact ((twice succ) 5))
(define compare (lambda (x y) (cond ((< x y) 'less) ((= x y) 'equal) (else 'greater))))
(compare 2 3)
(compare 7 (+ 2 3))
(define next (let ((cnt 0)) (lambda () (set! cnt (+ cnt 1)) cnt)))
(list (next) (next))
(define new-counter (lambda () (let ((x 0)) (lambda () (set! x (+ x 1)) x))))
(define c1 (new-counter))
(define c2 (new-counter))
(list (c1) (c1) (c2) (c1) (c2) (c1) (c1) (c2) (c2))
(let* ((x (+ 2 3)) (y (* x x)) (z (+ x y))) (list x y z))
(letrec ((even? (lambda (n) (if (= n 0) #t (odd? (- n 1)))))
(odd? (lambda (n) (if (= n 0) #f (even? (- n 1))))))
(even? 88))
(define (len l) (if (null? l) 0 (+ 1 (len (cdr l)))))
(len '(2 3 9))
(define (len2 l) (match l (() 0) ((x . t) (+ 1 (len2 t)))))
(len2 '(2 3 9))
(define (foo x) (match x
(('zero) 0)
(('succ x) (+ (foo x) 1))
(('plus x y) (+ (foo x) (foo y)))
(('minus x y) (- (foo x) (foo y)))
(x x)))
(foo '(plus (succ (zero)) (minus (succ (succ 5)) 3)))
(define (bar x) (match x
(['zero] 0)
(['succ x] (+ (bar x) 1))
(['plus x y] (+ (bar x) (bar y)))
(['minus x y] (- (bar x) (bar y)))
(['const x] x)))
(bar '[plus [succ [zero]] [minus [succ [succ [const 5]]] [const 3]]])
(define (map f l) (letrec
((map-f (lambda (l) (match l
(() ())
((h . t) (cons (f h) (map-f t)))))))
(map-f l)))
(map (lambda (x) (* x (+ 2 x))) '(2 3 9))
(define (make-promise proc) (let ((result-ready? #f) (result #f))
(lambda ()
(if result-ready? result
(let ((x (proc)))
(if result-ready? result
(begin (set! result x) (set! result-ready? #t) result)))))))
(define (force promise) (promise))
)
// */
// words for invoking Lisp definitions from Fift
// (args) def -- val
{ null rot map-quote cons lisp-dict @ rot run-definition
} : invoke-lisp-definition
{ atom lisp-dict-lookup 1 { @ invoke-lisp-definition }
} : (invoke-lisp)
{ bl word (invoke-lisp) } :: invoke-lisp
// ( 2 3 ) invoke-lisp compare .l
{ atom lisp-dict-lookup 2 { @ mklist-1 invoke-lisp-definition }
} : (invoke-lisp-fixed)
{ bl word (invoke-lisp-fixed) } :: invoke-lisp-fixed
// 9 8 2 invoke-lisp-fixed compare .l
{ bl word (invoke-lisp) does } : make-lisp-invoker
{ bl word (invoke-lisp-fixed) does } : make-lisp-fixed-invoker
// 2 make-lisp-fixed-invoker compare : compare
// 3 9 compare
// import Lisp definitions as Fift words
{ bl word dup (invoke-lisp) does swap 0 (create) } : import-lisp
{ bl word tuck (invoke-lisp-fixed) does swap 0 (create) } : import-lisp-fixed
// 1 import-lisp-fixed fact
// 7 fact .

184
crypto/fift/lib/Lists.fif Normal file
View file

@ -0,0 +1,184 @@
library Lists // List utilities
//
{ hole dup 1 { @ execute } does create } : recursive
// x x' -- ? recursively compares two S-expressions
recursive equal? {
dup tuple? {
over tuple? {
over count over count over = { // t t' l ?
0 { dup 0>= { 2dup [] 3 pick 2 pick [] equal? { 1+ } { drop -1 } cond
} if } rot times
nip nip 0>=
} { drop 2drop false } cond
} { 2drop false } cond
} { eqv? } cond
} swap !
// (a1 .. an) -- (an .. a1)
{ null swap { dup null? not } { uncons swap rot cons swap } while drop } : list-reverse
// (a1 .. an) -- an Computes last element of non-empty list l
{ { uncons dup null? { drop true } { nip false } cond } until } : list-last
// l l' -- l++l' Concatenates two lists
recursive list+ {
over null? { nip } { swap uncons rot list+ cons } cond
} swap !
// l l' -- l'' -1 or 0, where l = l' ++ l''
// Removes prefix from list
{ { dup null? { drop true true } {
swap dup null? { 2drop false true } { // l' l
uncons swap rot uncons -rot equal? { false } {
2drop false true
} cond } cond } cond } until
} : list-
// (a1 .. an) -- a1 .. an n Explodes a list
{ 0 { over null? not } { swap uncons rot 1+ } while nip } : explode-list
// (a1 .. an) x -- a1 .. an n x Explodes a list under the topmost element
{ swap explode-list dup 1+ roll } : explode-list-1
// l -- t Transforms a list into a tuple with the same elements
{ explode-list tuple } : list>tuple
// a1 ... an n x -- (a1 .. an) x
{ null swap rot { -rot cons swap } swap times } : mklist-1
// (s1 ... sn) -- s1+...+sn Concatenates a list of strings
{ "" { over null? not } { swap uncons -rot $+ } while nip
} : concat-string-list
// (x1 ... xn) -- x1+...+xn Sums a list of integers
{ 0 { over null? not } { swap uncons -rot + } while nip
} : sum-list
// (a1 ... an) a e -- e(...e(e(a,a1),a2),...),an)
{ -rot { over null? not } { swap uncons -rot 3 pick execute } while nip nip
} : foldl
// (a1 ... an) e -- e(...e(e(a1,a2),a3),...),an)
{ swap uncons swap rot foldl } : foldl-ne
// (a1 ... an) a e -- e(a1,e(a2,...,e(an,a)...))
recursive foldr {
rot dup null? { 2drop } {
uncons -rot 2swap swap 3 pick foldr rot execute
} cond
} swap !
// (a1 ... an) e -- e(a1,e(a2,...,e(a[n-1],an)...))
recursive foldr-ne {
over cdr null? { drop car } {
swap uncons 2 pick foldr-ne rot execute
} cond
} swap !
// (l1 ... ln) -- l1++...++ln Concatenates a list of lists
{ dup null? { ' list+ foldr-ne } ifnot } : concat-list-lists
// (a1 .. an . t) n -- t Computes the n-th tail of a list
{ ' cdr swap times } : list-tail
// (a0 .. an ..) n -- an Computes the n-th element of a list
{ list-tail car } : list-ref
// l -- ?
{ { dup null? { drop true true } {
dup pair? { cdr false } {
drop false true
} cond } cond } until
} : list?
// l -- n
{ 0 { over null? not } { 1+ swap uncons nip swap } while nip
} : list-length
// l e -- t // returns tail of l after first member that satisfies e
{ swap {
dup null? { nip true } {
tuck car over execute { drop true } {
swap cdr false
} cond } cond } until
} : list-tail-from
// a l -- t // tail of l after first occurence of a using eq?
{ swap 1 ' eq? does list-tail-from } : list-member-eq
{ swap 1 ' eqv? does list-tail-from } : list-member-eqv
{ swap 1 ' equal? does list-tail-from } : list-member-equal
// a l -- ?
{ list-member-eq null? not } : list-member?
{ list-member-eqv null? not } : list-member-eqv?
// l -- a -1 or 0 // returns car l if l is non-empty
{ dup null? { drop false } { car true } cond
} : safe-car
{ dup null? { drop false } { car second true } cond
} : get-first-value
// l e -- v -1 or 0
{ list-tail-from safe-car } : assoc-gen
{ list-tail-from get-first-value } : assoc-gen-x
// a l -- (a.v) -1 or 0 -- returns first entry (a . v) in l
{ swap 1 { swap first eq? } does assoc-gen } : assq
{ swap 1 { swap first eqv? } does assoc-gen } : assv
{ swap 1 { swap first equal? } does assoc-gen } : assoc
// a l -- v -1 or 0 -- returns v from first entry (a . v) in l
{ swap 1 { swap first eq? } does assoc-gen-x } : assq-val
{ swap 1 { swap first eqv? } does assoc-gen-x } : assv-val
{ swap 1 { swap first equal? } does assoc-gen-x } : assoc-val
// (a1 .. an) e -- (e(a1) .. e(an))
recursive list-map {
over null? { drop } {
swap uncons -rot over execute -rot list-map cons
} cond
} swap !
//
// create Lisp-style lists using words "(" and ")"
//
variable ')
'nop box constant ',
{ ") without (" abort } ') !
{ ') @ execute } : )
anon constant dot-marker
// m x1 ... xn t m -- (x1 ... xn . t)
{ swap
{ -rot 2dup eq? not }
{ over dot-marker eq? abort"invalid dotted list"
swap rot cons } while 2drop
} : list-tail-until-marker
// m x1 ... xn m -- (x1 ... xn)
{ null swap list-tail-until-marker } : list-until-marker
{ over dot-marker eq? { nip 2dup eq? abort"invalid dotted list" }
{ null swap } cond
list-tail-until-marker
} : list-until-marker-ext
{ ') @ ', @ } : ops-get
{ ', ! ') ! } : ops-set
{ anon dup ops-get 3 { ops-set list-until-marker-ext } does ') ! 'nop ', !
} : (
// test of Lisp-style lists
// ( 42 ( `+ 9 ( `* 3 4 ) ) "test" ) .l cr
// ( `eq? ( `* 3 4 ) 3 4 * ) .l cr
// `alpha ( `beta `gamma `delta ) cons .l cr
// { ( `eq? ( `* 3 5 pick ) 3 4 roll * ) } : 3*sample
// 17 3*sample .l cr
// similar syntax _( x1 .. xn ) for tuples
{ 2 { 1+ 2dup pick eq? } until 3 - nip } : count-to-marker
{ count-to-marker tuple nip } : tuple-until-marker
{ anon dup ops-get 3 { ops-set tuple-until-marker } does ') ! 'nop ', ! } : _(
// test of tuples
// _( _( 2 "two" ) _( 3 "three" ) _( 4 "four" ) ) .dump cr
// pseudo-Lisp tokenizer
"()[]'" 34 hold constant lisp-delims
{ lisp-delims 11 (word) } : lisp-token
{ null cons `quote swap cons } : do-quote
{ 1 { ', @ 2 { 2 { ', ! execute ', @ execute } does ', ! }
does ', ! } does
} : postpone-prefix
{ ', @ 1 { ', ! } does ', ! } : postpone-',
( `( ' ( pair
`) ' ) pair
`[ ' _( pair
`] ' ) pair
`' ' do-quote postpone-prefix pair
`. ' dot-marker postpone-prefix pair
`" { char " word } pair
`;; { 0 word drop postpone-', } pair
) constant lisp-token-dict
variable eol
{ eol @ eol 0! anon dup ') @ 'nop 3
{ ops-set list-until-marker-ext true eol ! } does ') ! rot ', !
{ lisp-token dup (number) dup { roll drop } {
drop atom dup lisp-token-dict assq { nip second execute } if
} cond
', @ execute
eol @
} until
-rot eol ! execute
} :_ List-generic(
{ 'nop 'nop List-generic( } :_ LIST(
// LIST((lambda (x) (+ x 1)) (* 3 4))
// LIST('(+ 3 4))
// LIST(2 3 "test" . 9)
// LIST((process '[plus 3 4]))

266
crypto/fift/lib/Stack.fif Normal file
View file

@ -0,0 +1,266 @@
library Stack // advanced stack manupulation library
"Lists.fif" include
// S(a b c - a c 2 a b) would compile to code performing the requested stack manipulation
// interface to low-level stack manipulation primitives
{ (number) 1- abort"index expected" dup 0 < over 255 > or
abort"index 0..255 expected"
} : (idx)
// push(n) : a0 .. an - a0 .. an a0 equivalent to "n pick"
// push(0) = dup, push(1) = over
{ 0 char ) word (idx) <push> } ::_ push(
// pop(n) : a0 a1 .. a(n-1) an - an a1 .. a(n-1)
// pop(0) = drop, pop(1) = nip
{ 0 char ) word (idx) <pop> } ::_ pop(
// xchg(i,j) : equivalent to "i j exch2"
{ 0 char , word (idx) char ) word (idx) <xchg> } ::_ xchg(
// xchg0(i) : equivalent to "i exch" or "xchg(0,i)"
// xchg0(1) = swap
{ 0 char ) word (idx) 0 <xchg> } ::_ xchg0(
forget (idx)
// parser for stack notation expressions
")" 34 hold +" -" constant stk-delims
anon constant stk-start
anon constant stk-to
variable stk-mode
{ stk-delims 11 (word) } : stk-token
'nop : mk-lit
// stk-start vn ... v0 -- stk-start ... v0 i where v[i]=v0
{ 0 {
1+ 2dup 2+ pick dup stk-start eq? { 2drop drop 0 true } { eqv? } cond
} until
} : stk-lookup
// stk-start a1 .. an stk-to b1 .. bm -- [a1 .. an] [b1 .. bm]
{ stk-mode @ 0= abort"identifier expected" } : chk-lit
{ stk-to list-until-marker stk-mode !
stk-start list-until-marker stk-mode @
stk-mode 0!
} : build-stk-effect
{ stk-start stk-mode 0! {
stk-token dup ")" $= { drop true } {
dup "-" $= {
drop stk-mode @ abort"duplicate -" true stk-mode ! stk-to false } {
dup 34 chr $= { chk-lit drop char " word mk-lit false } {
dup (number) ?dup { chk-lit 1- { swap mk-lit -rot } if mk-lit nip false } {
atom dup `_ eq? { stk-mode @ abort"identifier expected" false } {
stk-lookup 0= stk-mode @ = {
stk-mode @ { atom>$ +" -?" } { atom>$ +" redefined" } cond abort } {
false
} cond } cond } cond } cond } cond } cond } until
stk-mode @ 0= abort"'-' expected"
build-stk-effect
} :_ parse-stk-list(
// stack operation list construction
variable op-rlist
{ op-rlist null! } : clear-op-list
{ op-rlist @ list-reverse } : get-op-list
{ op-rlist @ cons op-rlist ! } : issue-op
{ minmax `xchg -rot triple } : op-xchg
{ `push swap pair } : op-push
{ `lit swap pair } : op-lit
{ `pop swap pair } : op-pop
0 op-pop constant op-drop
{ 2dup <> { op-xchg issue-op } if } : issue-xchg
{ op-push issue-op } : issue-push
{ op-lit issue-op } : issue-lit
{ op-pop issue-op } : issue-pop
{ op-drop issue-op } : issue-drop
{ ' issue-drop swap times } : issue-drop-#
// emulated stack contents
variable emul-stk
{ emul-stk @ count } : emul-depth
{ emul-depth 1- swap - } : adj-i
{ emul-depth 1- tuck swap - swap rot - swap } : adj-ij
// i j --
{ adj-ij 2dup emul-stk @ tuck swap [] swap rot [] rot // i sj si j
emul-stk @ -rot []= swap rot []= emul-stk !
} : emul-xchg
{ emul-stk @ tpop drop emul-stk ! } : emul-drop
// i --
{ 0 emul-xchg emul-drop } : emul-pop
// i -- s[i]
{ emul-stk @ swap [] } : emul-stk[]
// i -- si
{ adj-i emul-stk[] } : emul-get
{ 0 emul-get } : emul-tos
// v i -- ? Check whether s[i]=v
{ dup emul-depth < { emul-stk[] eqv? } { 2drop false } cond } : emul[]-eq?
// v -- i or -1 Returns maximum i with s[i]=v
{ emul-stk @ dup count { // v s i
?dup 0= { -1 true } { 1- 2dup [] 3 pick eqv? } cond // v s i' ?
} until nip nip
} : emul-stk-lookup-rev
// i --
{ emul-get emul-stk @ swap , emul-stk ! } : emul-push
{ emul-stk @ swap , emul-stk ! } : emul-lit
// show emulated stack contents similarly to .s
{ emul-stk @ explode dup 1 reverse ' .l swap times cr } : .e
// both issue an operation and emulate it
{ 2dup issue-xchg emul-xchg } : issue-emul-xchg
{ dup issue-push emul-push } : issue-emul-push
{ dup issue-lit emul-lit } : issue-emul-lit
{ dup issue-pop emul-pop } : issue-emul-pop
{ issue-drop emul-drop } : issue-emul-drop
{ ' issue-emul-drop swap times } : issue-emul-drop-#
// b.. s -- b.. s moves tos value to stk[s]
{ dup emul-stk[] 2 pick cdr list-member-eqv? {
dup adj-i 0 issue-emul-xchg } { dup adj-i issue-emul-pop } cond
} : move-tos-to
// new s -- ops registered
{ { over null? not } {
// .sl .e get-op-list .l cr
// get-op-list list-length 100 > abort"too long"
emul-depth over >
{ over emul-tos swap list-member-eqv? not } { false } cond {
// b.. s tos unneeded
issue-emul-drop } {
over car // b.. s b1
2dup swap emul[]-eq? { drop swap cdr swap 1+ } {
dup emul-stk-lookup-rev // b.. s b1 i
dup 0< { // b.. s b1 i not found, must be a literal
drop dup atom? abort"unavailable value"
issue-emul-lit } {
dup 3 pick < { // b.. s b1 i found in bottom s stack values
nip adj-i issue-emul-push // b.. s
dup emul-depth 1- < { move-tos-to } if
} {
emul-depth 1- over = { // b.. s b1 i found in tos
2drop move-tos-to
} { // b.. s b1 i
nip over adj-ij issue-emul-xchg
} cond } cond } cond } cond } cond } while
nip emul-depth swap - issue-emul-drop-#
} : generate-reorder-ops
// old new -- op-list
{ emul-stk @ op-rlist @ 2swap
swap list>tuple emul-stk ! clear-op-list
0 generate-reorder-ops get-op-list
-rot op-rlist ! emul-stk !
} : generate-reorder
{ parse-stk-list( generate-reorder } :_ SG(
// op-list rewriting according to a ruleset
// l f l1 l2 -- l' -1 or l f with l' = l2 + (l - l1)
{ push(3) rot list- { list+ nip nip true } { drop } cond
} : try-rule
// l f ll -- l' -1 or l f
{ { dup null? not } { uncons 3 -roll unpair try-rule rot } while drop
} : try-ruleset
// l ll -- l'
{ swap { over false swap try-ruleset 0= } until nip
} : try-ruleset*
// l ruleset -- l'
recursive try-ruleset*-everywhere {
tuck try-ruleset* dup null? { nip } {
uncons rot try-ruleset*-everywhere cons } cond
} swap !
LIST(
[([xchg 0 1] [xchg 0 2]) ([rot])]
[([xchg 0 1] [xchg 1 2]) ([-rot])]
[([xchg 0 2] [xchg 1 2]) ([rot])]
[([xchg 0 2] [xchg 0 1]) ([-rot])]
[([xchg 1 2] [xchg 0 1]) ([rot])]
[([xchg 1 2] [xchg 0 2]) ([-rot])]
[([xchg 0 1] [rot]) ([xchg 0 2])]
[([-rot] [xchg 0 1]) ([xchg 0 2])]
[([xchg 0 2] [xchg 1 3]) ([2swap])]
[([xchg 1 3] [xchg 0 2]) ([2swap])]
[([push 1] [push 1]) ([2dup])]
[([push 3] [push 3]) ([2over])]
[([pop 0] [pop 0]) ([2drop])]
[([pop 1] [pop 0]) ([2drop])]
[([xchg 0 1] [push 1]) ([tuck])]
[([rot] [-rot]) ()]
[([-rot] [rot]) ()]
) constant fift-stack-ruleset
{ fift-stack-ruleset try-ruleset*-everywhere } : fift-ops-rewrite
{ SG( fift-ops-rewrite } :_ SGF(
// helpers for creating Fift source strings for one fift-op
// i j -- s
{ minmax over { "xchg(" rot (.) $+ +"," swap (.) $+ +")" }
{ nip dup 1 = { drop "swap" } {
?dup { "xchg0(" swap (.) $+ +")" } { "" } cond
} cond } cond
} : source-<xchg>
// i -- s
{ dup 1 = { drop "over" } {
?dup { "push(" swap (.) $+ +")" } { "dup" } cond
} cond
} : source-<push>
// i -- s
{ dup 1 = { drop "nip" } {
?dup { "pop(" swap (.) $+ +")" } { "drop" } cond
} cond
} : source-<pop>
// lit -- s
{ dup string? { char " chr swap $+ char " hold } { (.) } cond
} : source-<lit>
// dictionary with all fift op compilation/source creation
{ 0 swap (compile) } : fop-compile
( _( `xchg 2 { <xchg> fop-compile } { source-<xchg> swap cons } )
_( `push 1 { <push> fop-compile } { source-<push> swap cons } )
_( `pop 1 { <pop> fop-compile } { source-<pop> swap cons } )
_( `lit 1 { 1 'nop (compile) } { source-<lit> swap cons } )
_( `rot 0 { ' rot fop-compile } { "rot" swap cons } )
_( `-rot 0 { ' -rot fop-compile } { "-rot" swap cons } )
_( `tuck 0 { ' tuck fop-compile } { "tuck" swap cons } )
_( `2swap 0 { ' 2swap fop-compile } { "2swap" swap cons } )
_( `2drop 0 { ' 2drop fop-compile } { "2drop" swap cons } )
_( `2dup 0 { ' 2dup fop-compile } { "2dup" swap cons } )
_( `2over 0 { ' 2over fop-compile } { "2over" swap cons } )
) box constant fift-op-dict
{ dup atom? { atom>$ } { drop "" } cond
"unknown operation " swap $+ abort
} : report-unknown-op
variable 'fop-entry-exec
// process fift-op according to 'fop-entry-exec
// ... op - ...
{ dup first dup fift-op-dict @ assq { report-unknown-op } ifnot
dup second 1+ push(3) count <> abort"incorrect param count"
nip swap explode dup roll drop 1- roll // o2 .. on entry
'fop-entry-exec @ execute
} : process-fift-op
// compile op-list into Fift wordlist
// wl op-list -- wl'
{ { third execute } 'fop-entry-exec !
swap ' process-fift-op foldl } : compile-fift-op*
// op-list -- e
{ fift-ops-rewrite ({) swap compile-fift-op* (}) } : ops>wdef
// S(<orig-stack> - <new-stack>) compiles a "word" performing required action
{ SG( ops>wdef 0 swap } ::_ S(
// 1 2 3 S(a b c - c a b a) .s would print 3 1 2 1
// transform op-list into Fift source
// ls op -- ls'
{ fift-ops-rewrite
{ 3 [] execute } 'fop-entry-exec !
null ' process-fift-op foldl
dup null? { drop "" } { { +" " swap $+ } foldr-ne } cond
} : ops>$
{ SG( ops>$ 1 'nop } ::_ $S(
{ SG( ops>$ type } :_ .$S(
// $S(a b c - b c a c a c) => string "rot 2dup over"
// S(a b c - b c a c a c) => compile/execute block { rot 2dup over }
// $S(_ x y _ - y x) => string "drop pop(2)"
// .$S(x1 x2 - 17 x1) => print string "drop 17 swap"
// simplify/transform sequences of stack manipulation operations
LIST(. [a b c d e f g h i j]) constant std-stack
{ stk-start std-stack explode drop stk-to std-stack explode drop
} : simplify<{
{ build-stk-effect generate-reorder ops>$ } : }>stack
// simplify<{ drop drop over over -13 }>stack => string "2drop 2dup -13"
// simplify<{ 17 rot }>stack => string "swap 17 swap"
// simplify<{ 5 1 reverse }>stack => string "xchg(1,5) xchg(2,4)"

125
crypto/fift/lib/TonUtil.fif Normal file
View file

@ -0,0 +1,125 @@
library TonUtil // TON Blockchain Fift Library
"Lists.fif" include
-1 constant Masterchain
0 constant Basechain
// parse workchain id
// ( S -- workchain )
{ (number) 1- abort"workchain id must be an integer"
dup 32 fits not abort"workchain id must fit in 32 bits"
} : parse-workchain-id
{ (number) 1- abort"integer expected" } : parse-int
// Private key load/generate
// ( fname -- pubkey privkey )
{ dup ."Loading private key from file " type cr
file>B dup Blen 32 <> abort"Private key must be exactly 32 bytes long"
dup priv>pub swap
} : load-keypair
// ( fname -- pubkey privkey )
{ dup file-exists?
{ load-keypair }
{ dup newkeypair swap rot over swap B>file
rot ."Saved new private key to file " type cr
} cond
} : load-generate-keypair
// Parse smart-contract address
// ( S -- workchain addr bounce? )
{ $>smca not abort"invalid smart-contract address"
1 and 0=
} : parse-smc-addr
// ( wc addr -- ) Show address in <workchain>:<account> form
{ swap ._ .":" x. } : .addr
// ( wc addr flags -- ) Show address in base64url form
{ smca>$ type } : .Addr
// ( wc addr fname -- ) Save address to file in 36-byte format
{ -rot 256 u>B swap 32 i>B B+ swap B>file } : save-address
// ( wc addr fname -- ) Save address and print message
{ dup ."(Saving address to file " type .")" cr save-address
} : save-address-verbose
// ( fname -- wc addr ) Load address from file
{ file>B 32 B|
dup Blen { 32 B>i@ } { drop Basechain } cond
swap 256 B>u@
} : load-address
// ( fname -- wc addr ) Load address from file and print message
{ dup ."(Loading address from file " type .")" cr load-address
} : load-address-verbose
// Parse string as address or load address from file (if string is prefixed by @)
// ( S default-bounce -- workchain addr bounce? )
{ over $len 0= abort"empty smart-contract address"
swap dup 1 $| swap "@" $=
{ nip load-address rot } { drop nip parse-smc-addr } cond
} : parse-load-address
// ( hex-str -- addr ) Parses ADNL address
{ dup $len 64 <> abort"ADNL address must consist of exactly 64 hexadecimal characters"
(hex-number) 1 <> abort"ADNL address must consist of 64 hexadecimal characters"
dup 256 ufits not abort"invalid ADNL address"
} : parse-adnl-address
// ( b wc addr -- b' ) Serializes address into Builder b
{ -rot 8 i, swap 256 u, } : addr,
// Gram utilities
1000000000 constant Gram
{ Gram swap */r } : Gram*/
{ Gram * } : Gram*
// ( S -- nanograms )
{ (number) ?dup 0= abort"not a valid Gram amount"
1- ' Gram*/ ' Gram* cond
} : $>GR
{ bl word $>GR 1 'nop } ::_ GR$
// ( nanograms -- S )
{ dup abs <# ' # 9 times char . hold #s rot sign #>
nip -trailing0 } : (.GR)
{ (.GR) ."GR$" type space } : .GR
// b x -- b' ( serializes a Gram amount )
{ -1 { 1+ 2dup 8 * ufits } until
rot over 4 u, -rot 8 * u, } : Gram,
// s -- x s' ( deserializes a Gram amount )
{ 4 u@+ swap 8 * u@+ } : Gram@+
// s -- x
{ 4 u@+ swap 8 * u@ } : Gram@
// currency collections
// b x --> b' ( serializes a VarUInteger32 )
{ -1 { 1+ 2dup 8 * ufits } until
rot over 5 u, -rot 8 * u, } : VarUInt32,
// s --> x ( deserializes a VarUInteger32 )
{ 5 u@+ swap 8 * u@ } : VarUInt32@
32 constant cc-key-bits
' VarUInt32, : val,
' VarUInt32@ : val@
// d k v -- d'
{ <b swap val, b> <s swap rot cc-key-bits idict! not abort"cannot add key-value to CurrencyCollection" } : +ccpair
dictnew constant cc0 // zero currency collection
// ( v k -- d ) Creates currency collection representing v units of currency k
{ cc0 swap rot +ccpair } : of-cc
{ dictnew { over null? not } { swap uncons -rot unpair +ccpair } while nip } : list>cc
{ dup null? { ."(null) " drop } { val@ . } cond } dup : .maybeVarUInt32 : .val
{ cc-key-bits { swap 32 1<< rmod . ."-> " .val ."; " true } dictforeach drop cr } : .cc
{ cc-key-bits { rot . ."-> " swap .val .val ."; " true } dictdiff drop cr } : show-cc-diff
{ cc-key-bits { val@ swap val@ + val, true } dictmerge } : cc+
{ null swap cc-key-bits { val@ pair swap cons true } dictforeach drop } : cc>list-rev
{ cc>list-rev list-reverse } : cc>list
forget val, forget val@ forget .val
// Libraries
// ( -- D ) New empty library collection
' dictnew : Libs{
// ( D -- D ) Return library collection as dictionary
'nop : }Libs
// ( D c x -- D' ) Add a public/private library c to collection D
{ <b swap 1 u, over ref, b> <s swap hash rot 256 udict!+
0= abort"duplicate library in collection" } : lib+
// ( D c -- D' ) Add private library c to collection D
{ 0 lib+ } : private_lib
// ( D c -- D' ) Add public library c to collection D
{ 1 lib+ } : public_lib

175
crypto/fift/utils.cpp Normal file
View file

@ -0,0 +1,175 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "utils.h"
#include "words.h"
#include "td/utils/PathView.h"
#include "td/utils/filesystem.h"
#include "td/utils/port/path.h"
namespace fift {
namespace {
std::string fift_dir(std::string dir) {
return dir.size() > 0 ? dir : td::PathView(td::realpath(__FILE__).move_as_ok()).parent_dir().str() + "lib/";
}
td::Result<std::string> load_source(std::string name, std::string dir = "") {
return td::read_file_str(fift_dir(dir) + name);
}
td::Result<std::string> load_Fift_fif(std::string dir = "") {
return load_source("Fift.fif", dir);
}
td::Result<std::string> load_Asm_fif(std::string dir = "") {
return load_source("Asm.fif", dir);
}
td::Result<std::string> load_TonUtil_fif(std::string dir = "") {
return load_source("TonUtil.fif", dir);
}
td::Result<std::string> load_Lists_fif(std::string dir = "") {
return load_source("Lists.fif", dir);
}
class MemoryFileLoader : public fift::FileLoader {
public:
td::Result<fift::FileLoader::File> read_file(td::CSlice filename) override {
auto it = files_.find(filename);
if (it == files_.end()) {
return td::Status::Error("File not found");
}
fift::FileLoader::File res;
res.data = it->second;
res.path = it->first;
return std::move(res);
}
td::Status write_file(td::CSlice filename, td::Slice data) override {
files_[filename.str()] = data.str();
return td::Status::OK();
}
void add_file(std::string path, std::string data) {
files_[path] = std::move(data);
}
td::Result<File> read_file_part(td::CSlice filename, td::int64 size, td::int64 offset) override {
auto it = files_.find(filename);
if (it == files_.end()) {
return td::Status::Error("File not found");
}
fift::FileLoader::File res;
if (static_cast<td::int64>(it->second.size()) < offset) {
return td::Status::Error("Offset too large");
}
if (size > static_cast<td::int64>(it->second.size())) {
size = static_cast<td::int64>(it->second.size());
}
res.data = td::Slice(it->second).substr(td::narrow_cast<size_t>(offset), td::narrow_cast<size_t>(size)).str();
res.path = it->first;
return std::move(res);
}
bool is_file_exists(td::CSlice filename) override {
return files_.count(filename) != 0;
}
private:
std::map<std::string, std::string, std::less<>> files_;
};
td::Result<fift::SourceLookup> create_source_lookup(std::string main, bool need_preamble = true, bool need_asm = true,
bool need_ton_util = true, std::string dir = "") {
auto loader = std::make_unique<MemoryFileLoader>();
loader->add_file("/main.fif", std::move(main));
if (need_preamble) {
TRY_RESULT(f, load_Fift_fif(dir));
loader->add_file("/Fift.fif", std::move(f));
}
if (need_asm) {
TRY_RESULT(f, load_Asm_fif(dir));
loader->add_file("/Asm.fif", std::move(f));
}
if (need_ton_util) {
{
TRY_RESULT(f, load_Lists_fif(dir));
loader->add_file("/Lists.fif", std::move(f));
}
{
TRY_RESULT(f, load_TonUtil_fif(dir));
loader->add_file("/TonUtil.fif", std::move(f));
}
}
auto res = fift::SourceLookup(std::move(loader));
res.add_include_path("/");
return std::move(res);
}
td::Result<fift::SourceLookup> run_fift(fift::SourceLookup source_lookup, std::ostream *stream,
bool preload_fift = true, std::vector<std::string> args = {}) {
fift::Fift::Config config;
config.source_lookup = std::move(source_lookup);
fift::init_words_common(config.dictionary);
fift::init_words_vm(config.dictionary);
fift::init_words_ton(config.dictionary);
config.error_stream = stream;
config.output_stream = stream;
if (args.size() != 0) {
std::vector<const char *> argv;
for (auto &arg : args) {
argv.push_back(arg.c_str());
}
fift::import_cmdline_args(config.dictionary, argv[0], td::narrow_cast<int>(argv.size() - 1), argv.data() + 1);
}
fift::Fift fift{std::move(config)};
if (preload_fift) {
TRY_STATUS(fift.interpret_file("Fift.fif", ""));
}
TRY_STATUS(fift.interpret_file("main.fif", ""));
return std::move(fift.config().source_lookup);
}
} // namespace
td::Result<FiftOutput> mem_run_fift(std::string source, std::vector<std::string> args, std::string fift_dir) {
std::stringstream ss;
TRY_RESULT(source_lookup, create_source_lookup(source, true, true, true, fift_dir));
TRY_RESULT_ASSIGN(source_lookup, run_fift(std::move(source_lookup), &ss, true, std::move(args)));
FiftOutput res;
res.source_lookup = std::move(source_lookup);
res.output = ss.str();
return std::move(res);
}
td::Result<FiftOutput> mem_run_fift(SourceLookup source_lookup, std::vector<std::string> args) {
std::stringstream ss;
TRY_RESULT_ASSIGN(source_lookup, run_fift(std::move(source_lookup), &ss, true, std::move(args)));
FiftOutput res;
res.source_lookup = std::move(source_lookup);
res.output = ss.str();
return std::move(res);
}
td::Result<fift::SourceLookup> create_mem_source_lookup(std::string main, std::string fift_dir, bool need_preamble,
bool need_asm, bool need_ton_util) {
return create_source_lookup(main, need_preamble, need_asm, need_ton_util, fift_dir);
}
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_dir) {
std::stringstream ss;
TRY_RESULT(source_lookup,
create_source_lookup(PSTRING() << "\"Asm.fif\" include\n<{ " << asm_code << "\n}>c boc>B \"res\" B>file",
true, true, true, fift_dir));
TRY_RESULT(res, run_fift(std::move(source_lookup), &ss));
TRY_RESULT(boc, res.read_file("res"));
return vm::std_boc_deserialize(std::move(boc.data));
}
} // namespace fift

35
crypto/fift/utils.h Normal file
View file

@ -0,0 +1,35 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "Fift.h"
#include <vector>
namespace fift {
struct FiftOutput {
SourceLookup source_lookup;
std::string output;
};
td::Result<fift::SourceLookup> create_mem_source_lookup(std::string main, std::string fift_dir = "",
bool need_preamble = true, bool need_asm = true,
bool need_ton_util = true);
td::Result<FiftOutput> mem_run_fift(std::string source, std::vector<std::string> args = {}, std::string fift_dir = "");
td::Result<FiftOutput> mem_run_fift(SourceLookup source_lookup, std::vector<std::string> args);
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_dir = "");
} // namespace fift

2747
crypto/fift/words.cpp Normal file

File diff suppressed because it is too large Load diff

43
crypto/fift/words.h Normal file
View file

@ -0,0 +1,43 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "Dictionary.h"
namespace fift {
// thrown by 'quit', 'bye' and 'halt' for exiting to top level
struct Quit {
int res;
Quit() : res(0) {
}
Quit(int _res) : res(_res) {
}
};
struct SkipToEof {};
void init_words_common(Dictionary& dictionary);
void init_words_vm(Dictionary& dictionary);
void init_words_ton(Dictionary& dictionary);
void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* const argv[]);
int funny_interpret_loop(IntCtx& ctx);
} // namespace fift

492
crypto/func/abscode.cpp Normal file
View file

@ -0,0 +1,492 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "func.h"
namespace funC {
/*
*
* ABSTRACT CODE
*
*/
TmpVar::TmpVar(var_idx_t _idx, int _cls, TypeExpr* _type, SymDef* sym, const SrcLocation* loc)
: v_type(_type), idx(_idx), cls(_cls), coord(0) {
if (sym) {
name = sym->sym_idx;
sym->value->idx = _idx;
}
if (loc) {
where = std::make_unique<SrcLocation>(*loc);
}
if (!_type) {
v_type = TypeExpr::new_hole();
}
}
void TmpVar::set_location(const SrcLocation& loc) {
if (where) {
*where = loc;
} else {
where = std::make_unique<SrcLocation>(loc);
}
}
void TmpVar::dump(std::ostream& os) const {
show(os);
os << " : " << v_type << " (width ";
v_type->show_width(os);
os << ")";
if (coord > 0) {
os << " = _" << (coord >> 8) << '.' << (coord & 255);
} else if (coord < 0) {
int n = (~coord >> 8), k = (~coord & 0xff);
if (k) {
os << " = (_" << n << ".._" << (n + k - 1) << ")";
} else {
os << " = ()";
}
}
os << std::endl;
}
void TmpVar::show(std::ostream& os, int omit_idx) const {
if (cls & _Named) {
os << sym::symbols.get_name(name);
if (omit_idx && (omit_idx >= 2 || (cls & _UniqueName))) {
return;
}
}
os << '_' << idx;
}
std::ostream& operator<<(std::ostream& os, const TmpVar& var) {
var.show(os);
return os;
}
void VarDescr::show_value(std::ostream& os) const {
if (val & _Int) {
os << 'i';
}
if (val & _Const) {
os << 'c';
}
if (val & _Zero) {
os << '0';
}
if (val & _NonZero) {
os << '!';
}
if (val & _Pos) {
os << '>';
}
if (val & _Neg) {
os << '<';
}
if (val & _Bool) {
os << 'B';
}
if (val & _Bit) {
os << 'b';
}
if (val & _Even) {
os << 'E';
}
if (val & _Odd) {
os << 'O';
}
if (val & _Finite) {
os << 'f';
}
if (val & _Nan) {
os << 'N';
}
if (int_const.not_null()) {
os << '=' << int_const;
}
}
void VarDescr::show(std::ostream& os, const char* name) const {
if (flags & _Last) {
os << '*';
}
if (flags & _Unused) {
os << '?';
}
if (name) {
os << name;
}
os << '_' << idx;
show_value(os);
}
void VarDescr::set_const(long long value) {
return set_const(td::RefInt256{true, value});
}
void VarDescr::set_const(td::RefInt256 value) {
int_const = std::move(value);
if (!int_const->signed_fits_bits(257)) {
int_const.write().invalidate();
}
val = _Const | _Int;
int s = sgn(int_const);
if (s < -1) {
val |= _Nan | _NonZero;
} else if (s < 0) {
val |= _NonZero | _Neg | _Finite;
if (*int_const == -1) {
val |= _Bool;
}
} else if (s > 0) {
val |= _NonZero | _Pos | _Finite;
} else if (!s) {
if (*int_const == 1) {
val |= _Bit;
}
val |= _Zero | _Neg | _Pos | _Finite | _Bool | _Bit;
}
if (val & _Finite) {
val |= int_const->get_bit(0) ? _Odd : _Even;
}
}
void VarDescr::set_const_nan() {
set_const(td::RefInt256{true});
}
void VarDescr::operator|=(const VarDescr& y) {
val &= y.val;
if (is_int_const() && cmp(int_const, y.int_const) != 0) {
val &= ~_Const;
}
if (!(val & _Const)) {
int_const.clear();
}
}
void VarDescr::operator&=(const VarDescr& y) {
val |= y.val;
if (y.int_const.not_null() && int_const.is_null()) {
int_const = y.int_const;
}
}
void VarDescr::set_value(const VarDescr& y) {
val = y.val;
int_const = y.int_const;
}
void VarDescr::set_value(VarDescr&& y) {
val = y.val;
int_const = std::move(y.int_const);
}
void VarDescr::clear_value() {
val = 0;
int_const.clear();
}
void VarDescrList::show(std::ostream& os) const {
os << "[";
for (const auto& v : list) {
os << ' ' << v;
}
os << " ]\n";
}
void Op::flags_set_clear(int set, int clear) {
flags = (flags | set) & ~clear;
for (auto& op : block0) {
op.flags_set_clear(set, clear);
}
for (auto& op : block1) {
op.flags_set_clear(set, clear);
}
}
void Op::split_vars(const std::vector<TmpVar>& vars) {
split_var_list(left, vars);
split_var_list(right, vars);
for (auto& op : block0) {
op.split_vars(vars);
}
for (auto& op : block1) {
op.split_vars(vars);
}
}
void Op::split_var_list(std::vector<var_idx_t>& var_list, const std::vector<TmpVar>& vars) {
int new_size = 0, changes = 0;
for (var_idx_t v : var_list) {
int c = vars.at(v).coord;
if (c < 0) {
++changes;
new_size += (~c & 0xff);
} else {
++new_size;
}
}
if (!changes) {
return;
}
std::vector<var_idx_t> new_var_list;
new_var_list.reserve(new_size);
for (var_idx_t v : var_list) {
int c = vars.at(v).coord;
if (c < 0) {
int n = (~c >> 8), k = (~c & 0xff);
while (k-- > 0) {
new_var_list.push_back(n++);
}
} else {
new_var_list.push_back(v);
}
}
var_list = std::move(new_var_list);
}
void Op::show(std::ostream& os, const std::vector<TmpVar>& vars, std::string pfx, int mode) const {
if (mode & 2) {
os << pfx << " [";
for (const auto& v : var_info.list) {
os << ' ';
if (v.flags & VarDescr::_Last) {
os << '*';
}
if (v.flags & VarDescr::_Unused) {
os << '?';
}
os << vars[v.idx];
if (mode & 4) {
os << ':';
v.show_value(os);
}
}
os << " ]\n";
}
std::string dis = disabled() ? "<disabled> " : "";
if (noreturn()) {
dis += "<noret> ";
}
if (!is_pure()) {
dis += "<impure> ";
}
switch (cl) {
case _Undef:
os << pfx << dis << "???\n";
break;
case _Nop:
os << pfx << dis << "NOP\n";
break;
case _Call:
os << pfx << dis << "CALL: ";
show_var_list(os, left, vars);
os << " := " << (fun_ref ? fun_ref->name() : "(null)") << " ";
if ((mode & 4) && args.size() == right.size()) {
show_var_list(os, args, vars);
} else {
show_var_list(os, right, vars);
}
os << std::endl;
break;
case _CallInd:
os << pfx << dis << "CALLIND: ";
show_var_list(os, left, vars);
os << " := EXEC ";
show_var_list(os, right, vars);
os << std::endl;
break;
case _Let:
os << pfx << dis << "LET ";
show_var_list(os, left, vars);
os << " := ";
show_var_list(os, right, vars);
os << std::endl;
break;
case _IntConst:
os << pfx << dis << "CONST ";
show_var_list(os, left, vars);
os << " := " << int_const << std::endl;
break;
case _Import:
os << pfx << dis << "IMPORT ";
show_var_list(os, left, vars);
os << std::endl;
break;
case _Return:
os << pfx << dis << "RETURN ";
show_var_list(os, left, vars);
os << std::endl;
break;
case _GlobVar:
os << pfx << dis << "GLOBVAR ";
show_var_list(os, left, vars);
os << " := " << (fun_ref ? fun_ref->name() : "(null)") << std::endl;
break;
case _Repeat:
os << pfx << dis << "REPEAT ";
show_var_list(os, left, vars);
os << ' ';
show_block(os, block0.get(), vars, pfx, mode);
os << std::endl;
break;
case _If:
os << pfx << dis << "IF ";
show_var_list(os, left, vars);
os << ' ';
show_block(os, block0.get(), vars, pfx, mode);
os << " ELSE ";
show_block(os, block1.get(), vars, pfx, mode);
os << std::endl;
break;
case _While:
os << pfx << dis << "WHILE ";
show_var_list(os, left, vars);
os << ' ';
show_block(os, block0.get(), vars, pfx, mode);
os << " DO ";
show_block(os, block1.get(), vars, pfx, mode);
os << std::endl;
break;
case _Until:
os << pfx << dis << "UNTIL ";
show_var_list(os, left, vars);
os << ' ';
show_block(os, block0.get(), vars, pfx, mode);
os << std::endl;
break;
case _Again:
os << pfx << dis << "AGAIN ";
show_var_list(os, left, vars);
os << ' ';
show_block(os, block0.get(), vars, pfx, mode);
os << std::endl;
break;
default:
os << pfx << dis << "<???" << cl << "> ";
show_var_list(os, left, vars);
os << " -- ";
show_var_list(os, right, vars);
os << std::endl;
break;
}
}
void Op::show_var_list(std::ostream& os, const std::vector<var_idx_t>& idx_list,
const std::vector<TmpVar>& vars) const {
if (!idx_list.size()) {
os << "()";
} else if (idx_list.size() == 1) {
os << vars.at(idx_list[0]);
} else {
os << "(" << vars.at(idx_list[0]);
for (std::size_t i = 1; i < idx_list.size(); i++) {
os << "," << vars.at(idx_list[i]);
}
os << ")";
}
}
void Op::show_var_list(std::ostream& os, const std::vector<VarDescr>& list, const std::vector<TmpVar>& vars) const {
auto n = list.size();
if (!n) {
os << "()";
} else {
os << "( ";
for (std::size_t i = 0; i < list.size(); i++) {
if (i) {
os << ", ";
}
if (list[i].is_unused()) {
os << '?';
}
os << vars.at(list[i].idx) << ':';
list[i].show_value(os);
}
os << " )";
}
}
void Op::show_block(std::ostream& os, const Op* block, const std::vector<TmpVar>& vars, std::string pfx, int mode) {
os << "{" << std::endl;
std::string pfx2 = pfx + " ";
for (const Op& op : block) {
op.show(os, vars, pfx2, mode);
}
os << pfx << "}";
}
void CodeBlob::flags_set_clear(int set, int clear) {
for (auto& op : ops) {
op.flags_set_clear(set, clear);
}
}
std::ostream& operator<<(std::ostream& os, const CodeBlob& code) {
code.print(os);
return os;
}
// flags: +1 = show variable definition locations; +2 = show vars after each op; +4 = show var abstract value info after each op; +8 = show all variables at start
void CodeBlob::print(std::ostream& os, int flags) const {
os << "CODE BLOB: " << var_cnt << " variables, " << in_var_cnt << " input\n";
if ((flags & 8) != 0) {
for (const auto& var : vars) {
var.dump(os);
if (var.where && (flags & 1) != 0) {
var.where->show(os);
os << " defined here:\n";
var.where->show_context(os);
}
}
}
os << "------- BEGIN --------\n";
for (const auto& op : ops) {
op.show(os, vars, "", flags);
}
os << "-------- END ---------\n\n";
}
var_idx_t CodeBlob::create_var(int cls, TypeExpr* var_type, SymDef* sym, const SrcLocation* location) {
vars.emplace_back(var_cnt, cls, var_type, sym, location);
if (sym) {
sym->value->idx = var_cnt;
}
return var_cnt++;
}
bool CodeBlob::import_params(FormalArgList arg_list) {
if (var_cnt || in_var_cnt || op_cnt) {
return false;
}
std::vector<var_idx_t> list;
for (const auto& par : arg_list) {
TypeExpr* arg_type;
SymDef* arg_sym;
SrcLocation arg_loc;
std::tie(arg_type, arg_sym, arg_loc) = par;
list.push_back(create_var(arg_sym ? (TmpVar::_In | TmpVar::_Named) : TmpVar::_In, arg_type, arg_sym, &arg_loc));
}
emplace_back(loc, Op::_Import, list);
in_var_cnt = var_cnt;
return true;
}
} // namespace funC

846
crypto/func/analyzer.cpp Normal file
View file

@ -0,0 +1,846 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "func.h"
namespace funC {
/*
*
* ANALYZE AND PREPROCESS ABSTRACT CODE
*
*/
void CodeBlob::simplify_var_types() {
for (TmpVar& var : vars) {
TypeExpr::remove_indirect(var.v_type);
}
}
int CodeBlob::split_vars(bool strict) {
int n = var_cnt, changes = 0;
for (int j = 0; j < var_cnt; j++) {
TmpVar& var = vars[j];
if (strict && var.v_type->minw != var.v_type->maxw) {
throw src::ParseError{var.where.get(), "variable does not have fixed width, cannot manipulate it"};
}
std::vector<TypeExpr*> comp_types;
int k = var.v_type->extract_components(comp_types);
assert(k <= 254 && n <= 0x7fff00);
assert((unsigned)k == comp_types.size());
if (k != 1) {
var.coord = ~((n << 8) + k);
for (int i = 0; i < k; i++) {
auto v = create_var(vars[j].cls, comp_types[i], 0, vars[j].where.get());
assert(v == n + i);
assert(vars[v].idx == v);
vars[v].name = vars[j].name;
vars[v].coord = ((int)j << 8) + i + 1;
}
n += k;
++changes;
} else if (strict && var.v_type->minw != 1) {
throw src::ParseError{var.where.get(),
"cannot work with variable or variable component of width greater than one"};
}
}
if (!changes) {
return 0;
}
for (auto& op : ops) {
op.split_vars(vars);
}
return changes;
}
bool CodeBlob::compute_used_code_vars() {
VarDescrList empty_var_info;
return compute_used_code_vars(ops, empty_var_info, true);
}
bool CodeBlob::compute_used_code_vars(std::unique_ptr<Op>& ops_ptr, const VarDescrList& var_info, bool edit) const {
assert(ops_ptr);
if (!ops_ptr->next) {
assert(ops_ptr->cl == Op::_Nop);
return ops_ptr->set_var_info(var_info);
}
return compute_used_code_vars(ops_ptr->next, var_info, edit) | ops_ptr->compute_used_vars(*this, edit);
}
bool operator==(const VarDescrList& x, const VarDescrList& y) {
if (x.size() != y.size()) {
return false;
}
for (std::size_t i = 0; i < x.size(); i++) {
if (x.list[i].idx != y.list[i].idx || x.list[i].flags != y.list[i].flags) {
return false;
}
}
return true;
}
bool same_values(const VarDescr& x, const VarDescr& y) {
if (x.val != y.val || x.int_const.is_null() != y.int_const.is_null()) {
return false;
}
if (x.int_const.not_null() && cmp(x.int_const, y.int_const) != 0) {
return false;
}
return true;
}
bool same_values(const VarDescrList& x, const VarDescrList& y) {
if (x.size() != y.size()) {
return false;
}
for (std::size_t i = 0; i < x.size(); i++) {
if (x.list[i].idx != y.list[i].idx || !same_values(x.list[i], y.list[i])) {
return false;
}
}
return true;
}
bool Op::set_var_info(const VarDescrList& new_var_info) {
if (var_info == new_var_info) {
return false;
}
var_info = new_var_info;
return true;
}
bool Op::set_var_info(VarDescrList&& new_var_info) {
if (var_info == new_var_info) {
return false;
}
var_info = std::move(new_var_info);
return true;
}
bool Op::set_var_info_except(const VarDescrList& new_var_info, const std::vector<var_idx_t>& var_list) {
if (!var_list.size()) {
return set_var_info(new_var_info);
}
VarDescrList tmp_info{new_var_info};
tmp_info -= var_list;
return set_var_info(new_var_info);
}
bool Op::set_var_info_except(VarDescrList&& new_var_info, const std::vector<var_idx_t>& var_list) {
if (var_list.size()) {
new_var_info -= var_list;
}
return set_var_info(std::move(new_var_info));
}
std::vector<var_idx_t> sort_unique_vars(const std::vector<var_idx_t>& var_list) {
std::vector<var_idx_t> vars{var_list}, unique_vars;
std::sort(vars.begin(), vars.end());
vars.erase(std::unique(vars.begin(), vars.end()), vars.end());
return vars;
}
VarDescr* VarDescrList::operator[](var_idx_t idx) {
auto it = std::lower_bound(list.begin(), list.end(), idx);
return it != list.end() && it->idx == idx ? &*it : nullptr;
}
const VarDescr* VarDescrList::operator[](var_idx_t idx) const {
auto it = std::lower_bound(list.begin(), list.end(), idx);
return it != list.end() && it->idx == idx ? &*it : nullptr;
}
std::size_t VarDescrList::count(const std::vector<var_idx_t> idx_list) const {
std::size_t res = 0;
for (var_idx_t idx : idx_list) {
if (operator[](idx)) {
++res;
}
}
return res;
}
std::size_t VarDescrList::count_used(const std::vector<var_idx_t> idx_list) const {
std::size_t res = 0;
for (var_idx_t idx : idx_list) {
auto v = operator[](idx);
if (v && !v->is_unused()) {
++res;
}
}
return res;
}
VarDescrList& VarDescrList::operator-=(var_idx_t idx) {
auto it = std::lower_bound(list.begin(), list.end(), idx);
if (it != list.end() && it->idx == idx) {
list.erase(it);
}
return *this;
}
VarDescrList& VarDescrList::operator-=(const std::vector<var_idx_t>& idx_list) {
for (var_idx_t idx : idx_list) {
*this -= idx;
}
return *this;
}
VarDescrList& VarDescrList::add_var(var_idx_t idx, bool unused) {
auto it = std::lower_bound(list.begin(), list.end(), idx);
if (it == list.end() || it->idx != idx) {
list.emplace(it, idx, VarDescr::_Last | (unused ? VarDescr::_Unused : 0));
} else if (it->is_unused() && !unused) {
it->clear_unused();
}
return *this;
}
VarDescrList& VarDescrList::add_vars(const std::vector<var_idx_t>& idx_list, bool unused) {
for (var_idx_t idx : idx_list) {
add_var(idx, unused);
}
return *this;
}
VarDescr& VarDescrList::add(var_idx_t idx) {
auto it = std::lower_bound(list.begin(), list.end(), idx);
if (it == list.end() || it->idx != idx) {
it = list.emplace(it, idx);
}
return *it;
}
VarDescr& VarDescrList::add_newval(var_idx_t idx) {
auto it = std::lower_bound(list.begin(), list.end(), idx);
if (it == list.end() || it->idx != idx) {
return *list.emplace(it, idx);
} else {
it->clear_value();
return *it;
}
}
VarDescrList& VarDescrList::clear_last() {
for (auto& var : list) {
if (var.flags & VarDescr::_Last) {
var.flags &= ~VarDescr::_Last;
}
}
return *this;
}
VarDescrList VarDescrList::operator+(const VarDescrList& y) const {
VarDescrList res;
auto it1 = list.cbegin();
auto it2 = y.list.cbegin();
while (it1 != list.cend() && it2 != y.list.cend()) {
if (it1->idx < it2->idx) {
res.list.push_back(*it1++);
} else if (it1->idx > it2->idx) {
res.list.push_back(*it2++);
} else {
res.list.push_back(*it1++);
res.list.back() += *it2++;
}
}
while (it1 != list.cend()) {
res.list.push_back(*it1++);
}
while (it2 != y.list.cend()) {
res.list.push_back(*it2++);
}
return res;
}
VarDescrList& VarDescrList::operator+=(const VarDescrList& y) {
return *this = *this + y;
}
VarDescrList VarDescrList::operator|(const VarDescrList& y) const {
VarDescrList res;
auto it1 = list.cbegin();
auto it2 = y.list.cbegin();
while (it1 != list.cend() && it2 != y.list.cend()) {
if (it1->idx < it2->idx) {
it1++;
} else if (it1->idx > it2->idx) {
it2++;
} else {
res.list.push_back(*it1++);
res.list.back() |= *it2++;
}
}
return res;
}
VarDescrList& VarDescrList::operator|=(const VarDescrList& y) {
return *this = *this | y;
}
VarDescrList& VarDescrList::operator&=(const VarDescrList& values) {
for (const VarDescr& vd : values.list) {
VarDescr* item = operator[](vd.idx);
if (item) {
*item &= vd;
}
}
return *this;
}
VarDescrList& VarDescrList::import_values(const VarDescrList& values) {
for (const VarDescr& vd : values.list) {
VarDescr* item = operator[](vd.idx);
if (item) {
item->set_value(vd);
}
}
return *this;
}
bool Op::std_compute_used_vars(bool disabled) {
// left = OP right
// var_info := (var_info - left) + right
VarDescrList new_var_info{next->var_info};
new_var_info -= left;
new_var_info.clear_last();
if (args.size() == right.size() && !disabled) {
for (const VarDescr& arg : args) {
new_var_info.add_var(arg.idx, arg.is_unused());
}
} else {
new_var_info.add_vars(right, disabled);
}
return set_var_info(std::move(new_var_info));
}
bool Op::compute_used_vars(const CodeBlob& code, bool edit) {
assert(next);
const VarDescrList& next_var_info = next->var_info;
if (cl == _Nop) {
return set_var_info_except(next_var_info, left);
}
switch (cl) {
case _IntConst:
case _GlobVar:
case _Call:
case _CallInd: {
// left = EXEC right;
if (!next_var_info.count_used(left) && is_pure()) {
// all variables in `left` are not needed
if (edit) {
disable();
}
return std_compute_used_vars(true);
}
return std_compute_used_vars();
}
case _Let: {
// left = right
std::size_t cnt = next_var_info.count_used(left);
assert(left.size() == right.size());
auto l_it = left.cbegin(), r_it = right.cbegin();
VarDescrList new_var_info{next_var_info};
new_var_info -= left;
new_var_info.clear_last();
std::vector<var_idx_t> new_left, new_right;
for (; l_it < left.cend(); ++l_it, ++r_it) {
if (std::find(l_it + 1, left.cend(), *l_it) == left.cend()) {
auto p = next_var_info[*l_it];
new_var_info.add_var(*r_it, !p || p->is_unused());
new_left.push_back(*l_it);
new_right.push_back(*r_it);
}
}
if (new_left.size() < left.size()) {
left = std::move(new_left);
right = std::move(new_right);
}
if (!cnt && edit) {
// all variables in `left` are not needed
disable();
}
return set_var_info(std::move(new_var_info));
}
case _Return: {
// return left
if (var_info.count(left) == left.size()) {
return false;
}
std::vector<var_idx_t> unique_vars = sort_unique_vars(left);
var_info.list.clear();
for (var_idx_t i : unique_vars) {
var_info.list.emplace_back(i, VarDescr::_Last);
}
return true;
}
case _Import: {
// import left
std::vector<var_idx_t> unique_vars = sort_unique_vars(left);
var_info.list.clear();
for (var_idx_t i : unique_vars) {
var_info.list.emplace_back(i, next_var_info[i] ? 0 : VarDescr::_Last);
}
return true;
}
case _If: {
// if (left) then block0 else block1
// VarDescrList nx_var_info = next_var_info;
// nx_var_info.clear_last();
code.compute_used_code_vars(block0, next_var_info, edit);
VarDescrList merge_info;
if (block1) {
code.compute_used_code_vars(block1, next_var_info, edit);
merge_info = block0->var_info + block1->var_info;
} else {
merge_info = block0->var_info + next_var_info;
}
merge_info.clear_last();
merge_info += left;
return set_var_info(std::move(merge_info));
}
case _While: {
// while (block0 || left) block1;
// ... { block0 left block1 } block0 left next
VarDescrList after_cond_first{next_var_info};
after_cond_first += left;
code.compute_used_code_vars(block0, after_cond_first, false);
VarDescrList new_var_info{block0->var_info};
bool changes = false;
do {
code.compute_used_code_vars(block1, block0->var_info, changes);
VarDescrList after_cond{block1->var_info};
after_cond += left;
code.compute_used_code_vars(block0, after_cond, changes);
std::size_t n = new_var_info.size();
new_var_info += block0->var_info;
new_var_info.clear_last();
if (changes) {
break;
}
changes = (new_var_info.size() == n);
} while (changes <= edit);
return set_var_info(std::move(new_var_info));
}
case _Until: {
// until (block0 || left);
// .. { block0 left } block0 left next
VarDescrList after_cond_first{next_var_info};
after_cond_first += left;
code.compute_used_code_vars(block0, after_cond_first, false);
VarDescrList new_var_info{block0->var_info};
bool changes = false;
do {
VarDescrList after_cond{new_var_info};
after_cond += next_var_info;
after_cond += left;
code.compute_used_code_vars(block0, after_cond, changes);
std::size_t n = new_var_info.size();
new_var_info += block0->var_info;
new_var_info.clear_last();
if (changes) {
break;
}
changes = (new_var_info.size() == n);
} while (changes <= edit);
return set_var_info(std::move(new_var_info) + next_var_info);
}
case _Repeat: {
// repeat (left) block0
// left { block0 } next
VarDescrList new_var_info{next_var_info};
bool changes = false;
do {
code.compute_used_code_vars(block0, new_var_info, changes);
std::size_t n = new_var_info.size();
new_var_info += block0->var_info;
new_var_info.clear_last();
if (changes) {
break;
}
changes = (new_var_info.size() == n);
} while (changes <= edit);
new_var_info += left;
return set_var_info(std::move(new_var_info));
}
case _Again: {
// for(;;) block0
// { block0 }
VarDescrList new_var_info;
bool changes = false;
do {
code.compute_used_code_vars(block0, new_var_info, changes);
std::size_t n = new_var_info.size();
new_var_info += block0->var_info;
new_var_info.clear_last();
if (changes) {
break;
}
changes = (new_var_info.size() == n);
} while (changes <= edit);
return set_var_info(std::move(new_var_info));
}
default:
std::cerr << "fatal: unknown operation <??" << cl << "> in compute_used_vars()\n";
throw src::ParseError{where, "unknown operation"};
}
}
bool prune_unreachable(std::unique_ptr<Op>& ops) {
if (!ops) {
return true;
}
Op& op = *ops;
if (op.cl == Op::_Nop) {
if (op.next) {
ops = std::move(op.next);
return prune_unreachable(ops);
}
return true;
}
bool reach;
switch (op.cl) {
case Op::_IntConst:
case Op::_GlobVar:
case Op::_Call:
case Op::_CallInd:
case Op::_Import:
reach = true;
break;
case Op::_Let: {
reach = true;
break;
}
case Op::_Return:
reach = false;
break;
case Op::_If: {
// if left then block0 else block1; ...
VarDescr* c_var = op.var_info[op.left[0]];
if (c_var && c_var->always_true()) {
op.block0->last().next = std::move(op.next);
ops = std::move(op.block0);
return prune_unreachable(ops);
} else if (c_var && c_var->always_false()) {
op.block1->last().next = std::move(op.next);
ops = std::move(op.block1);
return prune_unreachable(ops);
} else {
reach = prune_unreachable(op.block0) | prune_unreachable(op.block1);
}
break;
}
case Op::_While: {
// while (block0 || left) block1;
if (!prune_unreachable(op.block0)) {
// computation of block0 never returns
ops = std::move(op.block0);
return prune_unreachable(ops);
}
VarDescr* c_var = op.block0->last().var_info[op.left[0]];
if (c_var && c_var->always_false()) {
// block1 never executed
op.block0->last().next = std::move(op.next);
ops = std::move(op.block0);
return false;
} else if (c_var && c_var->always_true()) {
if (!prune_unreachable(op.block1)) {
// block1 never returns
op.block0->last().next = std::move(op.block1);
ops = std::move(op.block0);
return false;
}
// infinite loop
op.cl = Op::_Again;
op.block0->last().next = std::move(op.block1);
op.left.clear();
reach = false;
} else {
if (!prune_unreachable(op.block1)) {
// block1 never returns, while equivalent to block0 ; if left then block1 else next
op.cl = Op::_If;
std::unique_ptr<Op> new_op = std::move(op.block0);
op.block0 = std::move(op.block1);
op.block1 = std::make_unique<Op>(op.next->where, Op::_Nop);
new_op->last().next = std::move(ops);
ops = std::move(new_op);
}
reach = true; // block1 may be never executed
}
break;
}
case Op::_Repeat: {
// repeat (left) block0
VarDescr* c_var = op.var_info[op.left[0]];
if (c_var && c_var->always_nonpos()) {
// loop never executed
ops = std::move(op.next);
return prune_unreachable(ops);
}
if (c_var && c_var->always_pos()) {
if (!prune_unreachable(op.block0)) {
// block0 executed at least once, and it never returns
// replace code with block0
ops = std::move(op.block0);
return false;
}
} else {
prune_unreachable(op.block0);
}
reach = true;
break;
}
case Op::_Until:
case Op::_Again: {
// do block0 until left; ...
if (!prune_unreachable(op.block0)) {
// block0 never returns, replace loop by block0
ops = std::move(op.block0);
return false;
}
reach = true;
break;
}
default:
std::cerr << "fatal: unknown operation <??" << op.cl << ">\n";
throw src::ParseError{op.where, "unknown operation in prune_unreachable()"};
}
if (reach) {
return prune_unreachable(op.next);
} else {
while (op.next->next) {
op.next = std::move(op.next->next);
}
return false;
}
}
void CodeBlob::prune_unreachable_code() {
if (prune_unreachable(ops)) {
throw src::ParseError{loc, "control reaches end of function"};
}
}
void CodeBlob::fwd_analyze() {
VarDescrList values;
assert(ops && ops->cl == Op::_Import);
for (var_idx_t i : ops->left) {
values += i;
if (vars[i].v_type->is_int()) {
values[i]->val |= VarDescr::_Int;
}
}
ops->fwd_analyze(values);
}
void Op::prepare_args(VarDescrList values) {
if (args.size() != right.size()) {
args.clear();
for (var_idx_t i : right) {
args.emplace_back(i);
}
}
for (std::size_t i = 0; i < right.size(); i++) {
const VarDescr* val = values[right[i]];
if (val) {
args[i].set_value(*val);
args[i].clear_unused();
}
}
}
VarDescrList Op::fwd_analyze(VarDescrList values) {
var_info.import_values(values);
switch (cl) {
case _Nop:
case _Import:
break;
case _Return:
values.list.clear();
break;
case _IntConst: {
values.add_newval(left[0]).set_const(int_const);
break;
}
case _GlobVar:
case _Call: {
prepare_args(values);
auto func = dynamic_cast<const SymValAsmFunc*>(fun_ref->value);
if (func) {
std::vector<VarDescr> res;
res.reserve(left.size());
for (var_idx_t i : left) {
res.emplace_back(i);
}
AsmOpList tmp;
func->compile(tmp, res, args); // abstract interpretation of res := f (args)
int j = 0;
for (var_idx_t i : left) {
values.add_newval(i).set_value(res[j++]);
}
} else {
for (var_idx_t i : left) {
values.add_newval(i);
}
}
break;
}
case _CallInd: {
for (var_idx_t i : left) {
values.add_newval(i);
}
break;
}
case _Let: {
std::vector<VarDescr> old_val;
assert(left.size() == right.size());
for (std::size_t i = 0; i < right.size(); i++) {
const VarDescr* ov = values[right[i]];
if (!ov && verbosity >= 5) {
std::cerr << "FATAL: error in assignment at right component #" << i << " (no value for _" << right[i] << ")"
<< std::endl;
for (auto x : left) {
std::cerr << '_' << x << " ";
}
std::cerr << "= ";
for (auto x : right) {
std::cerr << '_' << x << " ";
}
std::cerr << std::endl;
}
// assert(ov);
if (ov) {
old_val.push_back(*ov);
} else {
old_val.emplace_back();
}
}
for (std::size_t i = 0; i < left.size(); i++) {
values.add_newval(left[i]).set_value(std::move(old_val[i]));
}
break;
}
case _If: {
VarDescrList val1 = block0->fwd_analyze(values);
VarDescrList val2 = block1 ? block1->fwd_analyze(std::move(values)) : std::move(values);
values = val1 | val2;
break;
}
case _Repeat: {
bool atl1 = (values[left[0]] && values[left[0]]->always_pos());
VarDescrList next_values = block0->fwd_analyze(values);
while (true) {
VarDescrList new_values = values | next_values;
if (same_values(new_values, values)) {
break;
}
values = std::move(new_values);
next_values = block0->fwd_analyze(values);
}
if (atl1) {
values = std::move(next_values);
}
break;
}
case _While: {
values = block0->fwd_analyze(values);
if (values[left[0]] && values[left[0]]->always_false()) {
// block1 never executed
block1->fwd_analyze(values);
break;
}
while (true) {
VarDescrList next_values = values | block0->fwd_analyze(block1->fwd_analyze(values));
if (same_values(next_values, values)) {
break;
}
values = std::move(next_values);
}
break;
}
case _Until:
case _Again: {
while (true) {
VarDescrList next_values = values | block0->fwd_analyze(values);
if (same_values(next_values, values)) {
break;
}
values = std::move(next_values);
}
values = block0->fwd_analyze(values);
break;
}
default:
std::cerr << "fatal: unknown operation <??" << cl << ">\n";
throw src::ParseError{where, "unknown operation in fwd_analyze()"};
}
if (next) {
return next->fwd_analyze(std::move(values));
} else {
return values;
}
}
bool Op::set_noreturn(bool nr) {
if (nr) {
flags |= _NoReturn;
} else {
flags &= ~_NoReturn;
}
return nr;
}
bool Op::mark_noreturn() {
switch (cl) {
case _Nop:
if (!next) {
return set_noreturn(false);
}
// fallthrough
case _Import:
case _IntConst:
case _Let:
case _GlobVar:
case _CallInd:
case _Call:
return set_noreturn(next->mark_noreturn());
case _Return:
return set_noreturn(true);
case _If:
return set_noreturn((block0->mark_noreturn() & (block1 && block1->mark_noreturn())) | next->mark_noreturn());
case _Again:
block0->mark_noreturn();
return set_noreturn(false);
case _Until:
return set_noreturn(block0->mark_noreturn() | next->mark_noreturn());
case _While:
block1->mark_noreturn();
return set_noreturn(block0->mark_noreturn() | next->mark_noreturn());
case _Repeat:
block0->mark_noreturn();
return set_noreturn(next->mark_noreturn());
default:
std::cerr << "fatal: unknown operation <??" << cl << ">\n";
throw src::ParseError{where, "unknown operation in mark_noreturn()"};
}
}
void CodeBlob::mark_noreturn() {
ops->mark_noreturn();
}
} // namespace funC

329
crypto/func/asmops.cpp Normal file
View file

@ -0,0 +1,329 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "parser/srcread.h"
#include "func.h"
#include <iostream>
namespace funC {
/*
*
* ASM-OP LIST FUNCTIONS
*
*/
int is_pos_pow2(td::RefInt256 x) {
if (sgn(x) > 0 && !sgn(x & (x - 1))) {
return x->bit_size(false) - 1;
} else {
return -1;
}
}
int is_neg_pow2(td::RefInt256 x) {
return sgn(x) < 0 ? is_pos_pow2(-x) : 0;
}
std::ostream& operator<<(std::ostream& os, AsmOp::SReg stack_reg) {
int i = stack_reg.idx;
if (i >= 0) {
if (i < 16) {
return os << 's' << i;
} else {
return os << i << " s()";
}
} else if (i >= -2) {
return os << "s(" << i << ')';
} else {
return os << i << " s()";
}
}
AsmOp AsmOp::Const(int arg, std::string push_op) {
std::ostringstream os;
os << arg << ' ' << push_op;
return AsmOp::Const(os.str());
}
AsmOp AsmOp::make_stk2(int a, int b, const char* str, int delta) {
std::ostringstream os;
os << SReg(a) << ' ' << SReg(b) << ' ' << str;
int c = std::max(a, b) + 1;
return AsmOp::Custom(os.str(), c, c + delta);
}
AsmOp AsmOp::make_stk3(int a, int b, int c, const char* str, int delta) {
std::ostringstream os;
os << SReg(a) << ' ' << SReg(b) << ' ' << SReg(c) << ' ' << str;
int m = std::max(a, std::max(b, c)) + 1;
return AsmOp::Custom(os.str(), m, m + delta);
}
AsmOp AsmOp::BlkSwap(int a, int b) {
std::ostringstream os;
if (a == 1 && b == 1) {
return AsmOp::Xchg(0, 1);
} else if (a == 1) {
if (b == 2) {
os << "ROT";
} else {
os << b << " ROLL";
}
} else if (b == 1) {
if (a == 2) {
os << "-ROT";
} else {
os << a << " -ROLL";
}
} else {
os << a << " " << b << " BLKSWAP";
}
return AsmOp::Custom(os.str(), a + b, a + b);
}
AsmOp AsmOp::BlkPush(int a, int b) {
std::ostringstream os;
if (a == 1) {
return AsmOp::Push(b);
} else if (a == 2 && b == 1) {
os << "2DUP";
} else {
os << a << " " << b << " BLKPUSH";
}
return AsmOp::Custom(os.str(), b + 1, a + b + 1);
}
AsmOp AsmOp::BlkDrop(int a) {
std::ostringstream os;
if (a == 1) {
return AsmOp::Pop();
} else if (a == 2) {
os << "2DROP";
} else {
os << a << " BLKDROP";
}
return AsmOp::Custom(os.str(), a, 0);
}
AsmOp AsmOp::BlkReverse(int a, int b) {
std::ostringstream os;
os << a << " " << b << " REVERSE";
return AsmOp::Custom(os.str(), a + b, a + b);
}
AsmOp AsmOp::IntConst(td::RefInt256 x) {
if (x->signed_fits_bits(8)) {
return AsmOp::Const(dec_string(std::move(x)) + " PUSHINT");
}
if (!x->is_valid()) {
return AsmOp::Const("PUSHNAN");
}
int k = is_pos_pow2(x);
if (k >= 0) {
return AsmOp::Const(k, "PUSHPOW2");
}
k = is_pos_pow2(x + 1);
if (k >= 0) {
return AsmOp::Const(k, "PUSHPOW2DEC");
}
k = is_pos_pow2(-x);
if (k >= 0) {
return AsmOp::Const(k, "PUSHNEGPOW2");
}
return AsmOp::Const(dec_string(std::move(x)) + " PUSHINT");
}
AsmOp AsmOp::Parse(std::string custom_op) {
if (custom_op == "NOP") {
return AsmOp::Nop();
} else if (custom_op == "SWAP") {
return AsmOp::Xchg(1);
} else if (custom_op == "DROP") {
return AsmOp::Pop(0);
} else if (custom_op == "NIP") {
return AsmOp::Pop(1);
} else if (custom_op == "DUP") {
return AsmOp::Push(0);
} else if (custom_op == "OVER") {
return AsmOp::Push(1);
} else {
return AsmOp::Custom(custom_op);
}
}
AsmOp AsmOp::Parse(std::string custom_op, int args, int retv) {
auto res = Parse(custom_op);
if (res.is_custom()) {
res.a = args;
res.b = retv;
}
return res;
}
void AsmOp::out(std::ostream& os) const {
if (!op.empty()) {
os << op;
return;
}
switch (t) {
case a_none:
break;
case a_xchg:
if (!a && !(b & -2)) {
os << (b ? "SWAP" : "NOP");
break;
}
os << "s" << a << " s" << b << " XCHG";
break;
case a_push:
if (!(a & -2)) {
os << (a ? "OVER" : "DUP");
break;
}
os << "s" << a << " PUSH";
break;
case a_pop:
if (!(a & -2)) {
os << (a ? "NIP" : "DROP");
break;
}
os << "s" << a << " POP";
break;
default:
throw src::Fatal{"unknown assembler operation"};
}
}
void AsmOp::out_indent_nl(std::ostream& os, bool no_eol) const {
for (int i = 0; i < indent; i++) {
os << " ";
}
out(os);
if (!no_eol) {
os << std::endl;
}
}
std::string AsmOp::to_string() const {
if (!op.empty()) {
return op;
} else {
std::ostringstream os;
out(os);
return os.str();
}
}
bool AsmOpList::append(const std::vector<AsmOp>& ops) {
for (const auto& op : ops) {
if (!append(op)) {
return false;
}
}
return true;
}
const_idx_t AsmOpList::register_const(Const new_const) {
if (new_const.is_null()) {
return not_const;
}
unsigned idx;
for (idx = 0; idx < constants_.size(); idx++) {
if (!td::cmp(new_const, constants_[idx])) {
return idx;
}
}
constants_.push_back(std::move(new_const));
return (const_idx_t)idx;
}
Const AsmOpList::get_const(const_idx_t idx) {
if ((unsigned)idx < constants_.size()) {
return constants_[idx];
} else {
return {};
}
}
void AsmOpList::show_var(std::ostream& os, var_idx_t idx) const {
if (!var_names_ || (unsigned)idx >= var_names_->size()) {
os << '_' << idx;
} else {
var_names_->at(idx).show(os, 2);
}
}
void AsmOpList::show_var_ext(std::ostream& os, std::pair<var_idx_t, const_idx_t> idx_pair) const {
auto i = idx_pair.first;
auto j = idx_pair.second;
if (!var_names_ || (unsigned)i >= var_names_->size()) {
os << '_' << i;
} else {
var_names_->at(i).show(os, 2);
}
if ((unsigned)j < constants_.size() && constants_[j].not_null()) {
os << '=' << constants_[j];
}
}
void AsmOpList::out(std::ostream& os, int mode) const {
if (!(mode & 2)) {
for (const auto& op : list_) {
op.out_indent_nl(os);
}
} else {
std::size_t n = list_.size();
for (std::size_t i = 0; i < n; i++) {
const auto& op = list_[i];
if (!op.is_comment() && i + 1 < n && list_[i + 1].is_comment()) {
op.out_indent_nl(os, true);
os << '\t';
do {
i++;
} while (i + 1 < n && list_[i + 1].is_comment());
list_[i].out(os);
os << std::endl;
} else {
op.out_indent_nl(os, false);
}
}
}
}
bool apply_op(StackTransform& trans, const AsmOp& op) {
if (!trans.is_valid()) {
return false;
}
switch (op.t) {
case AsmOp::a_none:
return true;
case AsmOp::a_xchg:
return trans.apply_xchg(op.a, op.b, true);
case AsmOp::a_push:
return trans.apply_push(op.a);
case AsmOp::a_pop:
return trans.apply_pop(op.a);
case AsmOp::a_const:
return !op.a && op.b == 1 && trans.apply_push_newconst();
default:
return false;
}
}
} // namespace funC

974
crypto/func/builtins.cpp Normal file
View file

@ -0,0 +1,974 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "func.h"
namespace funC {
using namespace std::literals::string_literals;
/*
*
* SYMBOL VALUES
*
*/
int glob_func_cnt, undef_func_cnt;
std::vector<SymDef*> glob_func;
SymDef* predefine_builtin_func(std::string name, TypeExpr* func_type) {
sym_idx_t name_idx = sym::symbols.lookup(name, 1);
if (sym::symbols.is_keyword(name_idx)) {
std::cerr << "fatal: global function `" << name << "` already defined as a keyword" << std::endl;
}
SymDef* def = sym::define_global_symbol(name_idx, true);
if (!def) {
std::cerr << "fatal: global function `" << name << "` already defined" << std::endl;
std::exit(1);
}
return def;
}
template <typename T>
void define_builtin_func(std::string name, TypeExpr* func_type, const T& func, bool impure = false) {
SymDef* def = predefine_builtin_func(name, func_type);
def->value = new SymValAsmFunc{func_type, func, impure};
}
template <typename T>
void define_builtin_func_x(std::string name, TypeExpr* func_type, const T& func, std::initializer_list<int> arg_order,
std::initializer_list<int> ret_order = {}, bool impure = false) {
SymDef* def = predefine_builtin_func(name, func_type);
def->value = new SymValAsmFunc{func_type, func, arg_order, ret_order, impure};
}
void define_builtin_func_x(std::string name, TypeExpr* func_type, const AsmOp& macro,
std::initializer_list<int> arg_order, std::initializer_list<int> ret_order = {},
bool impure = false) {
SymDef* def = predefine_builtin_func(name, func_type);
def->value = new SymValAsmFunc{func_type, make_simple_compile(macro), arg_order, ret_order, impure};
}
bool SymValAsmFunc::compile(AsmOpList& dest, std::vector<VarDescr>& in, std::vector<VarDescr>& out) const {
if (simple_compile) {
return dest.append(simple_compile(in, out));
} else if (ext_compile) {
return ext_compile(dest, in, out);
} else {
return false;
}
}
/*
*
* DEFINE BUILT-IN FUNCTIONS
*
*/
int emulate_negate(int a) {
int f = VarDescr::_Pos | VarDescr::_Neg;
if ((a & f) && (~a & f)) {
a ^= f;
}
f = VarDescr::_Bit | VarDescr::_Bool;
if ((a & f) && (~a & f)) {
a ^= f;
}
return a;
}
int emulate_add(int a, int b) {
if (b & VarDescr::_Zero) {
return a;
} else if (a & VarDescr::_Zero) {
return b;
}
int u = a & b, v = a | b;
int r = VarDescr::_Int;
int t = u & (VarDescr::_Pos | VarDescr::_Neg);
if (v & VarDescr::_Nan) {
return r | VarDescr::_Nan;
}
// non-quiet addition always returns finite results!
r |= t | VarDescr::_Finite;
if (t) {
r |= v & VarDescr::_NonZero;
}
r |= v & VarDescr::_Nan;
if (u & (VarDescr::_Odd | VarDescr::_Even)) {
r |= VarDescr::_Even;
} else if (!(~v & (VarDescr::_Odd | VarDescr::_Even))) {
r |= VarDescr::_Odd | VarDescr::_NonZero;
}
return r;
}
int emulate_sub(int a, int b) {
return emulate_add(a, emulate_negate(b));
}
int emulate_mul(int a, int b) {
if ((b & (VarDescr::_NonZero | VarDescr::_Bit)) == (VarDescr::_NonZero | VarDescr::_Bit)) {
return a;
} else if ((a & (VarDescr::_NonZero | VarDescr::_Bit)) == (VarDescr::_NonZero | VarDescr::_Bit)) {
return b;
}
int u = a & b, v = a | b;
int r = VarDescr::_Int;
if (v & VarDescr::_Nan) {
return r | VarDescr::_Nan;
}
// non-quiet multiplication always yields finite results, if any
r |= VarDescr::_Finite;
if (v & VarDescr::_Zero) {
// non-quiet multiplication
// the result is zero, if any result at all
return VarDescr::ConstZero;
}
if (u & (VarDescr::_Pos | VarDescr::_Neg)) {
r |= VarDescr::_Pos;
} else if (!(~v & (VarDescr::_Pos | VarDescr::_Neg))) {
r |= VarDescr::_Neg;
}
if (u & (VarDescr::_Bit | VarDescr::_Bool)) {
r |= VarDescr::_Bit;
} else if (!(~v & (VarDescr::_Bit | VarDescr::_Bool))) {
r |= VarDescr::_Bool;
}
r |= v & VarDescr::_Even;
r |= u & (VarDescr::_Odd | VarDescr::_NonZero);
return r;
}
int emulate_lshift(int a, int b) {
if (((a | b) & VarDescr::_Nan) || !(~b & (VarDescr::_Neg | VarDescr::_NonZero))) {
return VarDescr::_Int | VarDescr::_Nan;
}
if (b & VarDescr::_Zero) {
return a;
}
int t = ((b & VarDescr::_NonZero) ? VarDescr::_Even : 0);
t |= b & VarDescr::_Finite;
return emulate_mul(a, VarDescr::_Int | VarDescr::_Pos | VarDescr::_NonZero | VarDescr::_Even | t);
}
int emulate_div(int a, int b) {
if ((b & (VarDescr::_NonZero | VarDescr::_Bit)) == (VarDescr::_NonZero | VarDescr::_Bit)) {
return a;
} else if ((b & (VarDescr::_NonZero | VarDescr::_Bool)) == (VarDescr::_NonZero | VarDescr::_Bool)) {
return emulate_negate(b);
}
if (b & VarDescr::_Zero) {
return VarDescr::_Int | VarDescr::_Nan;
}
int u = a & b, v = a | b;
int r = VarDescr::_Int;
if (v & VarDescr::_Nan) {
return r | VarDescr::_Nan;
}
// non-quiet division always yields finite results, if any
r |= VarDescr::_Finite;
if (a & VarDescr::_Zero) {
// non-quiet division
// the result is zero, if any result at all
return VarDescr::ConstZero;
}
if (u & (VarDescr::_Pos | VarDescr::_Neg)) {
r |= VarDescr::_Pos;
} else if (!(~v & (VarDescr::_Pos | VarDescr::_Neg))) {
r |= VarDescr::_Neg;
}
if (u & (VarDescr::_Bit | VarDescr::_Bool)) {
r |= VarDescr::_Bit;
} else if (!(~v & (VarDescr::_Bit | VarDescr::_Bool))) {
r |= VarDescr::_Bool;
}
return r;
}
int emulate_rshift(int a, int b) {
if (((a | b) & VarDescr::_Nan) || !(~b & (VarDescr::_Neg | VarDescr::_NonZero))) {
return VarDescr::_Int | VarDescr::_Nan;
}
if (b & VarDescr::_Zero) {
return a;
}
int t = ((b & VarDescr::_NonZero) ? VarDescr::_Even : 0);
t |= b & VarDescr::_Finite;
return emulate_div(a, VarDescr::_Int | VarDescr::_Pos | VarDescr::_NonZero | VarDescr::_Even | t);
}
int emulate_mod(int a, int b, int round_mode = -1) {
if ((b & (VarDescr::_NonZero | VarDescr::_Bit)) == (VarDescr::_NonZero | VarDescr::_Bit)) {
return VarDescr::ConstZero;
} else if ((b & (VarDescr::_NonZero | VarDescr::_Bool)) == (VarDescr::_NonZero | VarDescr::_Bool)) {
return VarDescr::ConstZero;
}
if (b & VarDescr::_Zero) {
return VarDescr::_Int | VarDescr::_Nan;
}
int r = VarDescr::_Int;
if ((a | b) & VarDescr::_Nan) {
return r | VarDescr::_Nan;
}
// non-quiet division always yields finite results, if any
r |= VarDescr::_Finite;
if (a & VarDescr::_Zero) {
// non-quiet division
// the result is zero, if any result at all
return VarDescr::ConstZero;
}
if (round_mode < 0) {
r |= b & (VarDescr::_Pos | VarDescr::_Neg);
} else if (round_mode > 0) {
r |= emulate_negate(b) & (VarDescr::_Pos | VarDescr::_Neg);
}
if (a & (VarDescr::_Bit | VarDescr::_Bool)) {
if (r & VarDescr::_Pos) {
r |= VarDescr::_Bit;
}
if (r & VarDescr::_Neg) {
r |= VarDescr::_Bool;
}
}
if (b & VarDescr::_Even) {
r |= a & (VarDescr::_Even | VarDescr::_Odd);
}
return r;
}
bool VarDescr::always_less(const VarDescr& other) const {
if (is_int_const() && other.is_int_const()) {
return int_const < other.int_const;
}
return (always_nonpos() && other.always_pos()) || (always_neg() && other.always_nonneg());
}
bool VarDescr::always_leq(const VarDescr& other) const {
if (is_int_const() && other.is_int_const()) {
return int_const <= other.int_const;
}
return always_nonpos() && other.always_nonneg();
}
bool VarDescr::always_greater(const VarDescr& other) const {
return other.always_less(*this);
}
bool VarDescr::always_geq(const VarDescr& other) const {
return other.always_leq(*this);
}
bool VarDescr::always_equal(const VarDescr& other) const {
return is_int_const() && other.is_int_const() && *int_const == *other.int_const;
}
bool VarDescr::always_neq(const VarDescr& other) const {
if (is_int_const() && other.is_int_const()) {
return *int_const != *other.int_const;
}
return always_greater(other) || always_less(other) || (always_even() && other.always_odd()) ||
(always_odd() && other.always_even());
}
AsmOp exec_op(std::string op) {
return AsmOp::Custom(op);
}
AsmOp exec_op(std::string op, int args, int retv = 1) {
return AsmOp::Custom(op, args, retv);
}
AsmOp exec_arg_op(std::string op, long long arg) {
std::ostringstream os;
os << arg << ' ' << op;
return AsmOp::Custom(os.str());
}
AsmOp exec_arg_op(std::string op, long long arg, int args, int retv) {
std::ostringstream os;
os << arg << ' ' << op;
return AsmOp::Custom(os.str(), args, retv);
}
AsmOp exec_arg_op(std::string op, td::RefInt256 arg) {
std::ostringstream os;
os << arg << ' ' << op;
return AsmOp::Custom(os.str());
}
AsmOp exec_arg_op(std::string op, td::RefInt256 arg, int args, int retv) {
std::ostringstream os;
os << arg << ' ' << op;
return AsmOp::Custom(os.str(), args, retv);
}
AsmOp push_const(td::RefInt256 x) {
return AsmOp::IntConst(std::move(x));
}
AsmOp compile_add(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_add(x.val, y.val);
if (y.is_int_const() && y.int_const->signed_fits_bits(8)) {
y.unused();
if (y.always_zero()) {
return AsmOp::Nop();
}
if (*y.int_const == 1) {
return exec_op("INC", 1);
}
if (*y.int_const == -1) {
return exec_op("DEC", 1);
}
return exec_arg_op("ADDCONST", y.int_const, 1);
}
if (x.is_int_const() && x.int_const->signed_fits_bits(8)) {
x.unused();
if (x.always_zero()) {
return AsmOp::Nop();
}
if (*x.int_const == 1) {
return exec_op("INC", 1);
}
if (*x.int_const == -1) {
return exec_op("DEC", 1);
}
return exec_arg_op("ADDCONST", x.int_const, 1);
}
return exec_op("ADD", 2);
}
AsmOp compile_sub(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_sub(x.val, y.val);
if (y.is_int_const() && (-y.int_const)->signed_fits_bits(8)) {
y.unused();
if (y.always_zero()) {
return {};
}
if (*y.int_const == 1) {
return exec_op("DEC", 1);
}
if (*y.int_const == -1) {
return exec_op("INC", 1);
}
return exec_arg_op("ADDCONST", -y.int_const, 1);
}
if (x.always_zero()) {
x.unused();
return exec_op("NEGATE", 1);
}
return exec_op("SUB", 2);
}
AsmOp compile_negate(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_negate(x.val);
return exec_op("NEGATE", 1);
}
AsmOp compile_mul(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_mul(x.val, y.val);
if (y.is_int_const()) {
int k = is_pos_pow2(y.int_const);
if (y.int_const->signed_fits_bits(8) && k < 0) {
y.unused();
if (y.always_zero() && x.always_finite()) {
// dubious optimization: NaN * 0 = ?
r.set_const(y.int_const);
return push_const(r.int_const);
}
if (*y.int_const == 1 && x.always_finite()) {
return AsmOp::Nop();
}
if (*y.int_const == -1) {
return exec_op("NEGATE", 1);
}
return exec_arg_op("MULCONST", y.int_const, 1);
}
if (k >= 0) {
y.unused();
return exec_arg_op("LSHIFT#", k, 1);
}
}
if (x.is_int_const()) {
int k = is_pos_pow2(x.int_const);
if (x.int_const->signed_fits_bits(8) && k < 0) {
x.unused();
if (x.always_zero() && y.always_finite()) {
// dubious optimization: NaN * 0 = ?
r.set_const(x.int_const);
return push_const(r.int_const);
}
if (*x.int_const == 1 && y.always_finite()) {
return AsmOp::Nop();
}
if (*x.int_const == -1) {
return exec_op("NEGATE", 1);
}
return exec_arg_op("MULCONST", x.int_const, 1);
}
if (k >= 0) {
x.unused();
return exec_arg_op("LSHIFT#", k, 1);
}
}
return exec_op("MUL", 2);
}
AsmOp compile_lshift(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 (y.is_int_const()) {
auto yv = y.int_const->to_long();
if (yv < 0 || yv > 256) {
r.set_const_nan();
x.unused();
y.unused();
return push_const(r.int_const);
} else if (x.is_int_const()) {
r.set_const(x.int_const << (int)yv);
x.unused();
y.unused();
return push_const(r.int_const);
}
}
r.val = emulate_lshift(x.val, y.val);
if (y.is_int_const()) {
int k = (int)(y.int_const->to_long());
if (!k /* && x.always_finite() */) {
// dubious optimization: what if x=NaN ?
y.unused();
return AsmOp::Nop();
}
y.unused();
return exec_arg_op("LSHIFT#", k, 1);
}
if (x.is_int_const()) {
auto xv = x.int_const->to_long();
if (xv == 1) {
x.unused();
return exec_op("POW2", 1);
}
if (xv == -1) {
x.unused();
return exec_op("NEGPOW2", 1);
}
}
return exec_op("LSHIFT", 2);
}
AsmOp compile_rshift(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int round_mode) {
assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
if (y.is_int_const()) {
auto yv = y.int_const->to_long();
if (yv < 0 || yv > 256) {
r.set_const_nan();
x.unused();
y.unused();
return push_const(r.int_const);
} else if (x.is_int_const()) {
r.set_const(td::rshift(x.int_const, (int)yv, round_mode));
x.unused();
y.unused();
return push_const(r.int_const);
}
}
r.val = emulate_rshift(x.val, y.val);
std::string rshift = (round_mode < 0 ? "RSHIFT" : (round_mode ? "RSHIFTC" : "RSHIFTR"));
if (y.is_int_const()) {
int k = (int)(y.int_const->to_long());
if (!k /* && x.always_finite() */) {
// dubious optimization: what if x=NaN ?
y.unused();
return AsmOp::Nop();
}
y.unused();
return exec_arg_op(rshift + "#", k, 1);
}
return exec_op(rshift, 2);
}
AsmOp compile_div(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int round_mode) {
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(div(x.int_const, y.int_const, round_mode));
x.unused();
y.unused();
return push_const(r.int_const);
}
r.val = emulate_div(x.val, y.val);
if (y.is_int_const()) {
if (*y.int_const == 0) {
x.unused();
y.unused();
r.set_const(div(y.int_const, y.int_const));
return push_const(r.int_const);
}
if (*y.int_const == 1 && x.always_finite()) {
y.unused();
return AsmOp::Nop();
}
if (*y.int_const == -1) {
y.unused();
return exec_op("NEGATE", 1);
}
int k = is_pos_pow2(y.int_const);
if (k > 0) {
y.unused();
std::string op = "RSHIFT";
if (round_mode >= 0) {
op += (round_mode > 0 ? 'C' : 'R');
}
return exec_arg_op(op + '#', k, 1);
}
}
std::string op = "DIV";
if (round_mode >= 0) {
op += (round_mode > 0 ? 'C' : 'R');
}
return exec_op(op, 2);
}
AsmOp compile_mod(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int round_mode) {
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(mod(x.int_const, y.int_const, round_mode));
x.unused();
y.unused();
return push_const(r.int_const);
}
r.val = emulate_mod(x.val, y.val);
if (y.is_int_const()) {
if (*y.int_const == 0) {
x.unused();
y.unused();
r.set_const(mod(y.int_const, y.int_const));
return push_const(r.int_const);
}
if ((*y.int_const == 1 || *y.int_const == -1) && x.always_finite()) {
x.unused();
y.unused();
r.set_const(td::RefInt256{true, 0});
return push_const(r.int_const);
}
int k = is_pos_pow2(y.int_const);
if (k > 0) {
y.unused();
std::string op = "MODPOW2";
if (round_mode >= 0) {
op += (round_mode > 0 ? 'C' : 'R');
}
return exec_arg_op(op + '#', k, 1);
}
}
std::string op = "MOD";
if (round_mode >= 0) {
op += (round_mode > 0 ? 'C' : 'R');
}
return exec_op(op, 2);
}
int compute_compare(td::RefInt256 x, td::RefInt256 y, int mode) {
int s = td::cmp(x, y);
if (mode == 7) {
return s;
} else {
return (mode >> (1 - s)) & 1;
}
}
// return value:
// 4 -> constant 1
// 2 -> constant 0
// 1 -> constant -1
// 3 -> 0 or -1
int compute_compare(const VarDescr& x, const VarDescr& y, int mode) {
switch (mode) {
case 1: // >
return x.always_greater(y) ? 1 : (x.always_leq(y) ? 2 : 3);
case 2: // =
return x.always_equal(y) ? 1 : (x.always_neq(y) ? 2 : 3);
case 3: // >=
return x.always_geq(y) ? 1 : (x.always_less(y) ? 2 : 3);
case 4: // <
return x.always_less(y) ? 1 : (x.always_geq(y) ? 2 : 3);
case 5: // <>
return x.always_neq(y) ? 1 : (x.always_equal(y) ? 2 : 3);
case 6: // >=
return x.always_geq(y) ? 1 : (x.always_less(y) ? 2 : 3);
case 7: // <=>
return x.always_less(y)
? 1
: (x.always_equal(y)
? 2
: (x.always_greater(y)
? 4
: (x.always_leq(y) ? 3 : (x.always_geq(y) ? 6 : (x.always_neq(y) ? 5 : 7)))));
default:
return 7;
}
}
AsmOp compile_cmp_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int mode) {
assert(mode >= 1 && mode <= 7);
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(compute_compare(x.int_const, y.int_const, mode));
x.unused();
y.unused();
return push_const(r.int_const);
}
int v = compute_compare(x, y, mode);
assert(v);
if (!(v & (v - 1))) {
r.set_const(v - (v >> 2) - 2);
x.unused();
y.unused();
return push_const(r.int_const);
}
r.val = ~0;
if (v & 1) {
r.val &= VarDescr::ConstTrue;
}
if (v & 2) {
r.val &= VarDescr::ConstZero;
}
if (v & 4) {
r.val &= VarDescr::ConstOne;
}
static const char* cmp_int_names[] = {"", "GTINT", "EQINT", "GTINT", "LESSINT", "NEQINT", "LESSINT"};
static const char* cmp_names[] = {"", "GREATER", "EQUAL", "GEQ", "LESS", "NEQ", "LEQ", "CMP"};
static int cmp_int_delta[] = {0, 0, 0, -1, 0, 0, 1};
if (mode != 7) {
if (y.is_int_const() && y.int_const >= -128 && y.int_const <= 127) {
y.unused();
return exec_arg_op(cmp_int_names[mode], y.int_const + cmp_int_delta[mode], 1);
}
if (x.is_int_const() && x.int_const >= -128 && x.int_const <= 127) {
x.unused();
mode = ((mode & 4) >> 2) | (mode & 2) | ((mode & 1) << 2);
return exec_arg_op(cmp_int_names[mode], x.int_const + cmp_int_delta[mode], 1);
}
}
return exec_op(cmp_names[mode], 2);
}
AsmOp compile_throw(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
assert(res.empty() && args.size() == 1);
VarDescr& x = args[0];
if (x.is_int_const() && x.int_const->unsigned_fits_bits(11)) {
x.unused();
return exec_arg_op("THROW", x.int_const, 0, 0);
} else {
return exec_op("THROWANY", 1, 0);
}
}
AsmOp compile_cond_throw(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool mode) {
assert(res.empty() && args.size() == 2);
VarDescr &x = args[0], &y = args[1];
std::string suff = (mode ? "IF" : "IFNOT");
bool skip_cond = false;
if (y.always_true() || y.always_false()) {
y.unused();
skip_cond = true;
if (y.always_true() != mode) {
x.unused();
return AsmOp::Nop();
}
}
if (x.is_int_const() && x.int_const->unsigned_fits_bits(11)) {
x.unused();
return skip_cond ? exec_arg_op("THROW", x.int_const, 0, 0) : exec_arg_op("THROW"s + suff, x.int_const, 1, 0);
} else {
return skip_cond ? exec_op("THROWANY", 1, 0) : exec_arg_op("THROWANY"s + suff, 2, 0);
}
}
AsmOp compile_bool_const(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool val) {
assert(res.size() == 1 && args.empty());
VarDescr& r = res[0];
r.set_const(val ? -1 : 0);
return AsmOp::Const(val ? "TRUE" : "FALSE");
}
// (slice, int) load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
// (slice, int) load_uint(slice s, int len) asm( -> 1 0) "LDUX";
// int preload_int(slice s, int len) asm "PLDIX";
// int preload_uint(slice s, int len) asm "PLDUX";
AsmOp compile_fetch_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool fetch, bool sgnd) {
assert(args.size() == 2 && res.size() == 1 + (unsigned)fetch);
auto &y = args[1], &r = res.back();
r.val = (sgnd ? VarDescr::FiniteInt : VarDescr::FiniteUInt);
int v = -1;
if (y.is_int_const() && y.int_const >= 0 && y.int_const <= 256) {
v = (int)y.int_const->to_long();
if (!v) {
r.val = VarDescr::ConstZero;
}
if (v == 1) {
r.val = (sgnd ? VarDescr::ValBool : VarDescr::ValBit);
}
if (v > 0) {
y.unused();
return exec_arg_op((fetch ? "LD"s : "PLD"s) + (sgnd ? 'I' : 'U'), v, 1, 1 + (unsigned)fetch);
}
}
return exec_op((fetch ? "LD"s : "PLD"s) + (sgnd ? "IX" : "UX"), 2, 1 + (unsigned)fetch);
}
// builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
// builder store_int(builder b, int x, int len) asm(x b len) "STIX";
AsmOp compile_store_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool sgnd) {
assert(args.size() == 3 && res.size() == 1);
auto& z = args[2];
if (z.is_int_const() && z.int_const > 0 && z.int_const <= 256) {
z.unused();
return exec_arg_op("ST"s + (sgnd ? 'I' : 'U'), z.int_const, 2, 1);
}
return exec_op("ST"s + (sgnd ? "IX" : "UX"), 3, 1);
}
AsmOp compile_fetch_slice(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool fetch) {
assert(args.size() == 2 && res.size() == 1 + (unsigned)fetch);
auto& y = args[1];
int v = -1;
if (y.is_int_const() && y.int_const > 0 && y.int_const <= 256) {
v = (int)y.int_const->to_long();
if (v > 0) {
y.unused();
return exec_arg_op(fetch ? "LDSLICE" : "PLDSLICE", v, 1, 1 + (unsigned)fetch);
}
}
return exec_op(fetch ? "LDSLICEX" : "PLDSLICEX", 2, 1 + (unsigned)fetch);
}
// <type> <type>_at(tuple t, int index) asm "INDEXVAR";
AsmOp compile_tuple_at(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
assert(args.size() == 2 && res.size() == 1);
auto& y = args[1];
if (y.is_int_const() && y.int_const >= 0 && y.int_const < 16) {
y.unused();
return exec_arg_op("INDEX", y.int_const, 1, 1);
}
return exec_op("INDEXVAR", 2, 1);
}
// int null?(X arg)
AsmOp compile_is_null(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
assert(args.size() == 1 && res.size() == 1);
auto &x = args[0], &r = res[0];
if (x.always_null() || x.always_not_null()) {
x.unused();
r.set_const(x.always_null() ? -1 : 0);
return push_const(r.int_const);
}
res[0].val = VarDescr::ValBool;
return exec_op("ISNULL", 1, 1);
}
bool compile_run_method(AsmOpList& code, std::vector<VarDescr>& res, std::vector<VarDescr>& args, int n,
bool has_value) {
assert(args.size() == (unsigned)n + 1 && res.size() == (unsigned)has_value);
auto& x = args[0];
if (x.is_int_const() && x.int_const->unsigned_fits_bits(14)) {
x.unused();
code << exec_arg_op("PREPAREDICT", x.int_const, 0, 2);
} else {
code << exec_op("c3 PUSH", 0, 1);
}
code << exec_arg_op(has_value ? "1 CALLXARGS" : "0 CALLXARGS", n, n + 2, (unsigned)has_value);
return true;
}
void define_builtins() {
using namespace std::placeholders;
auto Unit = TypeExpr::new_unit();
auto Int = TypeExpr::new_atomic(_Int);
auto Cell = TypeExpr::new_atomic(_Cell);
auto Slice = TypeExpr::new_atomic(_Slice);
auto Builder = TypeExpr::new_atomic(_Builder);
// auto Null = TypeExpr::new_atomic(_Null);
auto Tuple = TypeExpr::new_atomic(_Tuple);
auto Int2 = TypeExpr::new_tensor({Int, Int});
auto Int3 = TypeExpr::new_tensor({Int, Int, Int});
auto TupleInt = TypeExpr::new_tensor({Tuple, Int});
auto SliceInt = TypeExpr::new_tensor({Slice, Int});
auto X = TypeExpr::new_var();
auto Y = TypeExpr::new_var();
auto Z = TypeExpr::new_var();
auto T = TypeExpr::new_var();
auto XY = TypeExpr::new_tensor({X, Y});
auto XYZ = TypeExpr::new_tensor({X, Y, Z});
auto XYZT = TypeExpr::new_tensor({X, Y, Z, T});
auto arith_bin_op = TypeExpr::new_map(Int2, Int);
auto arith_un_op = TypeExpr::new_map(Int, Int);
auto impure_bin_op = TypeExpr::new_map(Int2, Unit);
auto impure_un_op = TypeExpr::new_map(Int, Unit);
auto fetch_int_op = TypeExpr::new_map(SliceInt, SliceInt);
auto prefetch_int_op = TypeExpr::new_map(SliceInt, Int);
auto store_int_op = TypeExpr::new_map(TypeExpr::new_tensor({Builder, Int, Int}), Builder);
auto store_int_method =
TypeExpr::new_map(TypeExpr::new_tensor({Builder, Int, Int}), TypeExpr::new_tensor({Builder, Unit}));
auto fetch_slice_op = TypeExpr::new_map(SliceInt, TypeExpr::new_tensor({Slice, Slice}));
auto prefetch_slice_op = TypeExpr::new_map(SliceInt, Slice);
//auto arith_null_op = TypeExpr::new_map(TypeExpr::new_unit(), Int);
define_builtin_func("_+_", arith_bin_op, compile_add);
define_builtin_func("_-_", arith_bin_op, compile_sub);
define_builtin_func("-_", arith_un_op, compile_negate);
define_builtin_func("_*_", arith_bin_op, compile_mul);
define_builtin_func("_/_", arith_bin_op, std::bind(compile_div, _1, _2, -1));
define_builtin_func("_/~_", arith_bin_op, std::bind(compile_div, _1, _2, 0));
define_builtin_func("_/^_", arith_bin_op, std::bind(compile_div, _1, _2, 1));
define_builtin_func("_%_", arith_bin_op, std::bind(compile_mod, _1, _2, -1));
define_builtin_func("_%~_", arith_bin_op, std::bind(compile_mod, _1, _2, 0));
define_builtin_func("_%^_", arith_bin_op, std::bind(compile_mod, _1, _2, -1));
define_builtin_func("_/%_", TypeExpr::new_map(Int2, Int2), AsmOp::Custom("DIVMOD", 2, 2));
define_builtin_func("_<<_", arith_bin_op, compile_lshift);
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_add);
define_builtin_func("^_-=_", arith_bin_op, compile_sub);
define_builtin_func("^_*=_", arith_bin_op, compile_mul);
define_builtin_func("^_/=_", arith_bin_op, std::bind(compile_div, _1, _2, -1));
define_builtin_func("^_/~=_", arith_bin_op, std::bind(compile_div, _1, _2, 0));
define_builtin_func("^_/^=_", arith_bin_op, std::bind(compile_div, _1, _2, 1));
define_builtin_func("^_%=_", arith_bin_op, std::bind(compile_mod, _1, _2, -1));
define_builtin_func("^_%~=_", arith_bin_op, std::bind(compile_mod, _1, _2, 0));
define_builtin_func("^_%^=_", arith_bin_op, std::bind(compile_mod, _1, _2, 1));
define_builtin_func("^_<<=_", arith_bin_op, compile_lshift);
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("muldivr", TypeExpr::new_map(Int3, Int), AsmOp::Custom("MULDIVR", 3));
define_builtin_func("muldiv", TypeExpr::new_map(Int3, Int), AsmOp::Custom("MULDIV", 3));
define_builtin_func("muldivmod", TypeExpr::new_map(Int3, Int2), AsmOp::Custom("MULDIVMOD", 3, 2));
define_builtin_func("_==_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 2));
define_builtin_func("_!=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 5));
define_builtin_func("_<_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 4));
define_builtin_func("_>_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 1));
define_builtin_func("_<=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 6));
define_builtin_func("_>=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 3));
define_builtin_func("_<=>_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 7));
define_builtin_func("true", Int, /* AsmOp::Const("TRUE") */ std::bind(compile_bool_const, _1, _2, true));
define_builtin_func("false", Int, /* AsmOp::Const("FALSE") */ std::bind(compile_bool_const, _1, _2, false));
// define_builtin_func("null", Null, AsmOp::Const("PUSHNULL"));
define_builtin_func("nil", Tuple, AsmOp::Const("PUSHNULL"));
define_builtin_func("null?", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Int)), compile_is_null);
define_builtin_func("cons", TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor(X, Tuple), Tuple)),
AsmOp::Custom("CONS", 2, 1));
define_builtin_func("uncons", TypeExpr::new_forall({X}, TypeExpr::new_map(Tuple, TypeExpr::new_tensor(X, Tuple))),
AsmOp::Custom("UNCONS", 1, 2));
define_builtin_func_x("list_next",
TypeExpr::new_forall({X}, TypeExpr::new_map(Tuple, TypeExpr::new_tensor(Tuple, X))),
AsmOp::Custom("UNCONS", 1, 2), {}, {1, 0});
define_builtin_func("car", TypeExpr::new_forall({X}, TypeExpr::new_map(Tuple, X)), AsmOp::Custom("CAR", 1, 1));
define_builtin_func("cdr", TypeExpr::new_map(Tuple, Tuple), AsmOp::Custom("CDR", 1, 1));
define_builtin_func("pair", TypeExpr::new_forall({X, Y}, TypeExpr::new_map(XY, Tuple)), AsmOp::Custom("PAIR", 2, 1));
define_builtin_func("unpair", TypeExpr::new_forall({X, Y}, TypeExpr::new_map(Tuple, XY)),
AsmOp::Custom("UNPAIR", 1, 2));
define_builtin_func("triple", TypeExpr::new_forall({X, Y, Z}, TypeExpr::new_map(XYZ, Tuple)),
AsmOp::Custom("TRIPLE", 3, 1));
define_builtin_func("untriple", TypeExpr::new_forall({X, Y, Z}, TypeExpr::new_map(Tuple, XYZ)),
AsmOp::Custom("UNTRIPLE", 1, 3));
define_builtin_func("tuple4", TypeExpr::new_forall({X, Y, Z, T}, TypeExpr::new_map(XYZT, Tuple)),
AsmOp::Custom("4 TUPLE", 4, 1));
define_builtin_func("untuple4", TypeExpr::new_forall({X, Y, Z, T}, TypeExpr::new_map(Tuple, XYZT)),
AsmOp::Custom("4 UNTUPLE", 1, 4));
define_builtin_func("throw", impure_un_op, compile_throw, true);
define_builtin_func("throw_if", impure_bin_op, std::bind(compile_cond_throw, _1, _2, true), true);
define_builtin_func("throw_unless", impure_bin_op, std::bind(compile_cond_throw, _1, _2, false), true);
define_builtin_func_x("load_int", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, true), {}, {1, 0});
define_builtin_func_x("load_uint", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, false), {}, {1, 0});
define_builtin_func("preload_int", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, true));
define_builtin_func("preload_uint", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, false));
define_builtin_func_x("store_int", store_int_op, std::bind(compile_store_int, _1, _2, true), {1, 0, 2});
define_builtin_func_x("store_uint", store_int_op, std::bind(compile_store_int, _1, _2, false), {1, 0, 2});
define_builtin_func_x("~store_int", store_int_method, std::bind(compile_store_int, _1, _2, true), {1, 0, 2});
define_builtin_func_x("~store_uint", store_int_method, std::bind(compile_store_int, _1, _2, false), {1, 0, 2});
define_builtin_func_x("load_bits", fetch_slice_op, std::bind(compile_fetch_slice, _1, _2, true), {}, {1, 0});
define_builtin_func("preload_bits", prefetch_slice_op, std::bind(compile_fetch_slice, _1, _2, false));
define_builtin_func("int_at", TypeExpr::new_map(TupleInt, Int), compile_tuple_at);
define_builtin_func("cell_at", TypeExpr::new_map(TupleInt, Cell), compile_tuple_at);
define_builtin_func("slice_at", TypeExpr::new_map(TupleInt, Cell), compile_tuple_at);
define_builtin_func("tuple_at", TypeExpr::new_map(TupleInt, Tuple), compile_tuple_at);
define_builtin_func("at", TypeExpr::new_forall({X}, TypeExpr::new_map(TupleInt, X)), compile_tuple_at);
define_builtin_func("touch", TypeExpr::new_forall({X}, TypeExpr::new_map(X, X)), AsmOp::Nop());
define_builtin_func("~touch", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))),
AsmOp::Nop());
define_builtin_func("touch2", TypeExpr::new_forall({X, Y}, TypeExpr::new_map(XY, XY)), AsmOp::Nop());
define_builtin_func("~touch2", TypeExpr::new_forall({X, Y}, TypeExpr::new_map(XY, TypeExpr::new_tensor({XY, Unit}))),
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));
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_x(
"run_method1", TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X}), Unit)),
[](auto a, auto b, auto c) { return compile_run_method(a, b, c, 1, false); }, {1, 0}, {}, true);
define_builtin_func_x(
"run_method2", TypeExpr::new_forall({X, Y}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X, Y}), Unit)),
[](auto a, auto b, auto c) { return compile_run_method(a, b, c, 2, false); }, {1, 2, 0}, {}, true);
define_builtin_func_x(
"run_method3", TypeExpr::new_forall({X, Y, Z}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X, Y, Z}), Unit)),
[](auto a, auto b, auto c) { return compile_run_method(a, b, c, 3, false); }, {1, 2, 3, 0}, {}, true);
}
} // namespace funC

689
crypto/func/codegen.cpp Normal file
View file

@ -0,0 +1,689 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "func.h"
namespace funC {
/*
*
* GENERATE TVM STACK CODE
*
*/
StackLayout Stack::vars() const {
StackLayout res;
res.reserve(s.size());
for (auto x : s) {
res.push_back(x.first);
}
return res;
}
int Stack::find(var_idx_t var, int from) const {
for (int i = from; i < depth(); i++) {
if (at(i).first == var) {
return i;
}
}
return -1;
}
// finds var in [from .. to)
int Stack::find(var_idx_t var, int from, int to) const {
for (int i = from; i < depth() && i < to; i++) {
if (at(i).first == var) {
return i;
}
}
return -1;
}
// finds var outside [from .. to)
int Stack::find_outside(var_idx_t var, int from, int to) const {
from = std::max(from, 0);
if (from >= to) {
return find(var);
} else {
int t = find(var, 0, from);
return t >= 0 ? t : find(var, to);
}
}
int Stack::find_const(const_idx_t cst, int from) const {
for (int i = from; i < depth(); i++) {
if (at(i).second == cst) {
return i;
}
}
return -1;
}
void Stack::forget_const() {
for (auto& vc : s) {
if (vc.second != not_const) {
vc.second = not_const;
}
}
}
void Stack::issue_pop(int i) {
validate(i);
o << AsmOp::Pop(i);
at(i) = get(0);
s.pop_back();
modified();
}
void Stack::issue_push(int i) {
validate(i);
o << AsmOp::Push(i);
s.push_back(get(i));
modified();
}
void Stack::issue_xchg(int i, int j) {
validate(i);
validate(j);
if (i != j && get(i) != get(j)) {
o << AsmOp::Xchg(i, j);
std::swap(at(i), at(j));
modified();
}
}
int Stack::drop_vars_except(const VarDescrList& var_info, int excl_var) {
int dropped = 0, changes;
do {
changes = 0;
int n = depth();
for (int i = 0; i < n; i++) {
var_idx_t idx = at(i).first;
if (((!var_info[idx] || var_info[idx]->is_unused()) && idx != excl_var) || find(idx, 0, i - 1) >= 0) {
// unneeded
issue_pop(i);
changes = 1;
break;
}
}
dropped += changes;
} while (changes);
return dropped;
}
void Stack::show(int flags) {
std::ostringstream os;
for (auto i : s) {
os << ' ';
o.show_var_ext(os, i);
}
o << AsmOp::Comment(os.str());
mode |= _Shown;
}
void Stack::forget_var(var_idx_t idx) {
for (auto& x : s) {
if (x.first == idx) {
x = std::make_pair(_Garbage, not_const);
modified();
}
}
}
void Stack::push_new_var(var_idx_t idx) {
forget_var(idx);
s.emplace_back(idx, not_const);
modified();
}
void Stack::push_new_const(var_idx_t idx, const_idx_t cidx) {
forget_var(idx);
s.emplace_back(idx, cidx);
modified();
}
void Stack::assign_var(var_idx_t new_idx, var_idx_t old_idx) {
int i = find(old_idx);
assert(i >= 0 && "variable not found in stack");
if (new_idx != old_idx) {
at(i).first = new_idx;
modified();
}
}
void Stack::do_copy_var(var_idx_t new_idx, var_idx_t old_idx) {
int i = find(old_idx);
assert(i >= 0 && "variable not found in stack");
if (find(old_idx, i + 1) < 0) {
issue_push(i);
assert(at(0).first == old_idx);
}
assign_var(new_idx, old_idx);
}
void Stack::enforce_state(const StackLayout& req_stack) {
int k = (int)req_stack.size();
for (int i = 0; i < k; i++) {
var_idx_t x = req_stack[i];
if (i < depth() && s[i].first == x) {
continue;
}
int j = find(x);
if (j >= depth() - i) {
issue_push(j);
j = 0;
}
issue_xchg(j, depth() - i - 1);
assert(s[i].first == x);
}
while (depth() > k) {
issue_pop(0);
}
assert(depth() == k);
for (int i = 0; i < k; i++) {
assert(s[i].first == req_stack[i]);
}
}
void Stack::merge_const(const Stack& req_stack) {
assert(s.size() == req_stack.s.size());
for (std::size_t i = 0; i < s.size(); i++) {
assert(s[i].first == req_stack.s[i].first);
if (s[i].second != req_stack.s[i].second) {
s[i].second = not_const;
}
}
}
void Stack::merge_state(const Stack& req_stack) {
enforce_state(req_stack.vars());
merge_const(req_stack);
}
void Stack::rearrange_top(const StackLayout& top, std::vector<bool> last) {
while (last.size() < top.size()) {
last.push_back(false);
}
int k = (int)top.size();
for (int i = 0; i < k; i++) {
for (int j = i + 1; j < k; j++) {
if (top[i] == top[j]) {
last[i] = false;
break;
}
}
}
int ss = 0;
for (int i = 0; i < k; i++) {
if (last[i]) {
++ss;
}
}
for (int i = 0; i < k; i++) {
var_idx_t x = top[i];
// find s(j) containing x with j not in [ss, ss+i)
int j = find_outside(x, ss, ss + i);
if (last[i]) {
// rearrange x to be at s(ss-1)
issue_xchg(--ss, j);
assert(get(ss).first == x);
} else {
// create a new copy of x
issue_push(j);
issue_xchg(0, ss);
assert(get(ss).first == x);
}
}
assert(!ss);
}
void Stack::rearrange_top(var_idx_t top, bool last) {
int i = find(top);
if (last) {
issue_xchg(0, i);
} else {
issue_push(i);
}
assert(get(0).first == top);
}
bool Op::generate_code_step(Stack& stack) {
stack.opt_show();
stack.drop_vars_except(var_info);
stack.opt_show();
const auto& next_var_info = next->var_info;
switch (cl) {
case _Nop:
case _Import:
return true;
case _Return: {
stack.enforce_state(left);
stack.opt_show();
return false;
}
case _IntConst: {
auto p = next_var_info[left[0]];
if (!p || p->is_unused()) {
return true;
}
auto cidx = stack.o.register_const(int_const);
int i = stack.find_const(cidx);
if (i < 0) {
stack.o << push_const(int_const);
stack.push_new_const(left[0], cidx);
} else {
assert(stack.at(i).second == cidx);
stack.do_copy_var(left[0], stack[i]);
}
return true;
}
case _GlobVar: {
assert(left.size() == 1);
auto p = next_var_info[left[0]];
if (!p || p->is_unused() || disabled()) {
return true;
}
auto func = dynamic_cast<const SymValAsmFunc*>(fun_ref->value);
if (func) {
std::vector<VarDescr> res;
res.reserve(left.size());
for (var_idx_t i : left) {
res.emplace_back(i);
}
func->compile(stack.o, res, args); // compile res := f (args)
} else {
std::string name = sym::symbols.get_name(fun_ref->sym_idx);
stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size());
}
stack.push_new_var(left[0]);
return true;
}
case _Let: {
assert(left.size() == right.size());
int i = 0;
std::vector<bool> active;
active.reserve(left.size());
for (std::size_t k = 0; k < left.size(); k++) {
var_idx_t y = left[k]; // "y" = "x"
auto p = next_var_info[y];
active.push_back(p && !p->is_unused());
}
for (std::size_t k = 0; k < left.size(); k++) {
if (!active[k]) {
continue;
}
var_idx_t x = right[k]; // "y" = "x"
bool is_last = true;
for (std::size_t l = k + 1; l < right.size(); l++) {
if (right[l] == x && active[l]) {
is_last = false;
}
}
if (is_last) {
auto info = var_info[x];
is_last = (info && info->is_last());
}
if (is_last) {
stack.assign_var(--i, x);
} else {
stack.do_copy_var(--i, x);
}
}
i = 0;
for (std::size_t k = 0; k < left.size(); k++) {
if (active[k]) {
stack.assign_var(left[k], --i);
}
}
return true;
}
case _Call:
case _CallInd: {
if (disabled()) {
return true;
}
SymValFunc* func = (fun_ref ? dynamic_cast<SymValFunc*>(fun_ref->value) : nullptr);
auto arg_order = (func ? func->get_arg_order() : nullptr);
auto ret_order = (func ? func->get_ret_order() : nullptr);
assert(!arg_order || arg_order->size() == right.size());
assert(!ret_order || ret_order->size() == left.size());
std::vector<var_idx_t> right1;
if (args.size()) {
assert(args.size() == right.size());
for (int i = 0; i < (int)right.size(); i++) {
int j = arg_order ? arg_order->at(i) : i;
const VarDescr& arg = args.at(j);
if (!arg.is_unused()) {
assert(var_info[arg.idx] && !var_info[arg.idx]->is_unused());
right1.push_back(arg.idx);
}
}
} else if (arg_order) {
for (int i = 0; i < (int)right.size(); i++) {
right1.push_back(right.at(arg_order->at(i)));
}
} else {
right1 = right;
}
std::vector<bool> last;
for (var_idx_t x : right1) {
last.push_back(var_info[x] && var_info[x]->is_last());
}
stack.rearrange_top(right1, std::move(last));
stack.opt_show();
int k = (int)stack.depth() - (int)right1.size();
assert(k >= 0);
for (int i = 0; i < (int)right1.size(); i++) {
if (stack.s[k + i].first != right1[i]) {
std::cerr << stack.o;
}
assert(stack.s[k + i].first == right1[i]);
}
if (cl == _CallInd) {
stack.o << exec_arg_op("CALLARGS", (int)right.size() - 1, -1, (int)right.size() - 1);
} else {
auto func = dynamic_cast<const SymValAsmFunc*>(fun_ref->value);
if (func) {
std::vector<VarDescr> res;
res.reserve(left.size());
for (var_idx_t i : left) {
res.emplace_back(i);
}
func->compile(stack.o, res, args); // compile res := f (args)
} else {
std::string name = sym::symbols.get_name(fun_ref->sym_idx);
stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size());
}
}
stack.s.resize(k);
for (int i = 0; i < (int)left.size(); i++) {
int j = ret_order ? ret_order->at(i) : i;
stack.push_new_var(left.at(j));
}
return true;
}
case _If: {
if (block0->is_empty() && block1->is_empty()) {
return true;
}
var_idx_t x = left[0];
stack.rearrange_top(x, var_info[x] && var_info[x]->is_last());
assert(stack[0] == x);
stack.opt_show();
stack.s.pop_back();
stack.modified();
if (block1->is_empty()) {
// if (left) block0; ...
if (block0->noreturn()) {
stack.o << "IFJMP:<{";
stack.o.indent();
Stack stack_copy{stack};
block0->generate_code_all(stack_copy);
stack.o.undent();
stack.o << "}>";
return true;
}
stack.o << "IF:<{";
stack.o.indent();
Stack stack_copy{stack};
block0->generate_code_all(stack_copy);
stack_copy.drop_vars_except(var_info);
stack_copy.opt_show();
if (stack_copy == stack) {
stack.o.undent();
stack.o << "}>";
return true;
}
stack_copy.drop_vars_except(next->var_info);
stack_copy.opt_show();
if (stack_copy.vars() == stack.vars()) {
stack.o.undent();
stack.o << "}>";
stack.merge_const(stack_copy);
return true;
}
stack.o.undent();
stack.o << "}>ELSE<{";
stack.o.indent();
stack.merge_state(stack_copy);
stack.opt_show();
stack.o.undent();
stack.o << "}>";
return true;
}
if (block0->is_empty()) {
// if (!left) block1; ...
if (block1->noreturn()) {
stack.o << "IFNOTJMP:<{";
stack.o.indent();
Stack stack_copy{stack};
block1->generate_code_all(stack_copy);
stack.o.undent();
stack.o << "}>";
return true;
}
stack.o << "IFNOT:<{";
stack.o.indent();
Stack stack_copy{stack};
block1->generate_code_all(stack_copy);
stack_copy.drop_vars_except(var_info);
stack_copy.opt_show();
if (stack_copy.vars() == stack.vars()) {
stack.o.undent();
stack.o << "}>";
stack.merge_const(stack_copy);
return true;
}
stack_copy.drop_vars_except(next->var_info);
stack_copy.opt_show();
if (stack_copy.vars() == stack.vars()) {
stack.o.undent();
stack.o << "}>";
stack.merge_const(stack_copy);
return true;
}
stack.o.undent();
stack.o << "}>ELSE<{";
stack.o.indent();
stack.merge_state(stack_copy);
stack.opt_show();
stack.o.undent();
stack.o << "}>";
return true;
}
if (block0->noreturn()) {
stack.o << "IFJMP:<{";
stack.o.indent();
Stack stack_copy{stack};
block0->generate_code_all(stack_copy);
stack.o.undent();
stack.o << "}>";
return block1->generate_code_all(stack);
}
if (block1->noreturn()) {
stack.o << "IFNOTJMP:<{";
stack.o.indent();
Stack stack_copy{stack};
block1->generate_code_all(stack_copy);
stack.o.undent();
stack.o << "}>";
return block0->generate_code_all(stack);
}
stack.o << "IF:<{";
stack.o.indent();
Stack stack_copy{stack};
block0->generate_code_all(stack_copy);
stack_copy.drop_vars_except(next->var_info);
stack_copy.opt_show();
stack.o.undent();
stack.o << "}>ELSE<{";
stack.o.indent();
block1->generate_code_all(stack);
stack.merge_state(stack_copy);
stack.opt_show();
stack.o.undent();
stack.o << "}>";
return true;
}
case _Repeat: {
var_idx_t x = left[0];
//stack.drop_vars_except(block0->var_info, x);
stack.rearrange_top(x, var_info[x] && var_info[x]->is_last());
assert(stack[0] == x);
stack.opt_show();
stack.s.pop_back();
stack.modified();
if (!next->is_empty()) {
stack.o << "REPEAT:<{";
stack.o.indent();
stack.forget_const();
StackLayout layout1 = stack.vars();
block0->generate_code_all(stack);
stack.enforce_state(std::move(layout1));
stack.opt_show();
stack.o.undent();
stack.o << "}>";
return true;
} else {
stack.o << "REPEATEND";
stack.forget_const();
StackLayout layout1 = stack.vars();
block0->generate_code_all(stack);
stack.enforce_state(std::move(layout1));
stack.opt_show();
return false;
}
}
case _Again: {
stack.drop_vars_except(block0->var_info);
stack.opt_show();
if (!next->is_empty()) {
stack.o << "AGAIN:<{";
stack.o.indent();
stack.forget_const();
StackLayout layout1 = stack.vars();
block0->generate_code_all(stack);
stack.enforce_state(std::move(layout1));
stack.opt_show();
stack.o.undent();
stack.o << "}>";
return true;
} else {
stack.o << "AGAINEND";
stack.forget_const();
StackLayout layout1 = stack.vars();
block0->generate_code_all(stack);
stack.enforce_state(std::move(layout1));
stack.opt_show();
return false;
}
}
case _Until: {
// stack.drop_vars_except(block0->var_info);
// stack.opt_show();
if (!next->is_empty()) {
stack.o << "UNTIL:<{";
stack.o.indent();
stack.forget_const();
auto layout1 = stack.vars();
block0->generate_code_all(stack);
layout1.push_back(left[0]);
stack.enforce_state(std::move(layout1));
stack.opt_show();
stack.o.undent();
stack.o << "}>";
stack.s.pop_back();
stack.modified();
return true;
} else {
stack.o << "UNTILEND";
stack.forget_const();
StackLayout layout1 = stack.vars();
block0->generate_code_all(stack);
layout1.push_back(left[0]);
stack.enforce_state(std::move(layout1));
stack.opt_show();
return false;
}
}
case _While: {
// while (block0 | left) block1; ...next
var_idx_t x = left[0];
stack.drop_vars_except(block0->var_info);
stack.opt_show();
StackLayout layout1 = stack.vars();
bool next_empty = next->is_empty();
stack.o << (next_empty ? "WHILEEND:<{" : "WHILE:<{");
stack.o.indent();
stack.forget_const();
block0->generate_code_all(stack);
stack.rearrange_top(x, !next->var_info[x] && !block1->var_info[x]);
stack.opt_show();
stack.s.pop_back();
stack.modified();
stack.o.undent();
Stack stack_copy{stack};
stack.o << (next_empty ? "}>" : "}>DO<{");
if (!next_empty) {
stack.o.indent();
}
stack_copy.opt_show();
block1->generate_code_all(stack_copy);
stack_copy.enforce_state(std::move(layout1));
stack_copy.opt_show();
if (!next_empty) {
stack.o.undent();
stack.o << "}>";
return true;
} else {
return false;
}
}
default:
std::cerr << "fatal: unknown operation <??" << cl << ">\n";
throw src::ParseError{where, "unknown operation in generate_code()"};
}
}
bool Op::generate_code_all(Stack& stack) {
if (generate_code_step(stack) && next) {
return next->generate_code_all(stack);
} else {
return false;
}
}
void CodeBlob::generate_code(AsmOpList& out, int mode) {
Stack stack{out, mode};
assert(ops && ops->cl == Op::_Import);
for (var_idx_t x : ops->left) {
stack.push_new_var(x);
}
ops->generate_code_all(stack);
if (!(mode & Stack::_DisableOpt)) {
optimize_code(out);
}
}
void CodeBlob::generate_code(std::ostream& os, int mode, int indent) {
AsmOpList out_list(indent, &vars);
generate_code(out_list, mode);
out_list.out(os, mode);
}
} // namespace funC

248
crypto/func/func.cpp Normal file
View file

@ -0,0 +1,248 @@
/*
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-2019 Telegram Systems LLP
*/
#include "func.h"
#include "parser/srcread.h"
#include "parser/lexer.h"
#include "parser/symtable.h"
#include <getopt.h>
#include <fstream>
namespace funC {
int verbosity, indent, opt_level = 2;
bool stack_layout_comments, op_rewrite_comments, program_envelope, asm_preamble;
std::ostream* outs = &std::cout;
std::string generated_from, boc_output_filename;
/*
*
* OUTPUT CODE GENERATOR
*
*/
void generate_output_func(SymDef* func_sym) {
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;
}
if (!func_val->code) {
std::cerr << "( function `" << name << "` undefined )\n";
} else {
CodeBlob& code = *(func_val->code);
if (verbosity >= 3) {
code.print(std::cerr, 9);
}
code.simplify_var_types();
// std::cerr << "after simplify_var_types: \n"; code.print(std::cerr, 0);
code.prune_unreachable_code();
// std::cerr << "after prune_unreachable: \n"; code.print(std::cerr, 0);
code.split_vars(true);
// std::cerr << "after split_vars: \n"; code.print(std::cerr, 0);
for (int i = 0; i < 8; i++) {
code.compute_used_code_vars();
if (verbosity >= 5) {
std::cerr << "after compute_used_vars: \n";
code.print(std::cerr, 6);
}
code.fwd_analyze();
// std::cerr << "after fwd_analyze: \n"; code.print(std::cerr, 6);
code.prune_unreachable_code();
// std::cerr << "after prune_unreachable: \n"; code.print(std::cerr, 6);
}
code.mark_noreturn();
if (verbosity >= 3) {
code.print(std::cerr, 15);
}
if (verbosity >= 2) {
std::cerr << "\n---------- resulting code for " << name << " -------------\n";
}
*outs << std::string(indent * 2, ' ') << name << " PROC:<{\n";
code.generate_code(
*outs,
(stack_layout_comments ? Stack::_StkCmt | Stack::_CptStkCmt : 0) | (opt_level < 2 ? Stack::_DisableOpt : 0),
indent + 1);
*outs << std::string(indent * 2, ' ') << "}>\n";
if (verbosity >= 2) {
std::cerr << "--------------\n";
}
}
}
int generate_output() {
if (asm_preamble) {
*outs << "\"Asm.fif\" include\n";
}
*outs << "// automatically generated from " << generated_from << std::endl;
if (program_envelope) {
*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, ' ');
if (func_val->method_id.is_null()) {
*outs << "DECLPROC " << name << "\n";
} else {
*outs << func_val->method_id << " DECLMETHOD " << name << "\n";
}
}
int errors = 0;
for (SymDef* func_sym : glob_func) {
try {
generate_output_func(func_sym);
} catch (src::Error& err) {
std::cerr << "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";
}
if (!boc_output_filename.empty()) {
*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";
std::exit(2);
}
std::string output_filename;
int main(int argc, char* const argv[]) {
int i;
bool interactive = false;
while ((i = getopt(argc, argv, "Ahi:Io:O:PRSvW:")) != -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 'h':
default:
usage(argv[0]);
}
}
if (funC::program_envelope && !funC::indent) {
funC::indent = 1;
}
funC::define_keywords();
funC::define_builtins();
int ok = 0, proc = 0;
try {
while (optind < argc) {
funC::generated_from += std::string{"`"} + argv[optind] + "` ";
ok += funC::parse_source_file(argv[optind++]);
proc++;
}
if (interactive) {
funC::generated_from += "stdin ";
ok += funC::parse_source_stdin();
proc++;
}
if (ok < proc) {
throw src::Fatal{"output code generation omitted because of errors"};
}
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 2;
}
funC::outs = fs.get();
}
funC::generate_output();
} catch (src::Fatal& fatal) {
std::cerr << "fatal: " << fatal << std::endl;
std::exit(1);
} catch (src::Error& error) {
std::cerr << error << std::endl;
std::exit(1);
} catch (funC::UnifyError& unif_err) {
std::cerr << "fatal: ";
unif_err.print_message(std::cerr);
std::cerr << std::endl;
std::exit(1);
}
}

1445
crypto/func/func.h Normal file

File diff suppressed because it is too large Load diff

325
crypto/func/gen-abscode.cpp Normal file
View file

@ -0,0 +1,325 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "func.h"
namespace funC {
/*
*
* EXPRESSIONS
*
*/
Expr* Expr::copy() const {
auto res = new Expr{*this};
for (auto& arg : res->args) {
arg = arg->copy();
}
return res;
}
Expr::Expr(int c, sym_idx_t name_idx, std::initializer_list<Expr*> _arglist) : cls(c), args(std::move(_arglist)) {
sym = sym::lookup_symbol(name_idx);
if (!sym) {
}
}
void Expr::chk_rvalue(const Lexem& lem) const {
if (!is_rvalue()) {
lem.error_at("rvalue expected before `", "`");
}
}
void Expr::chk_lvalue(const Lexem& lem) const {
if (!is_lvalue()) {
lem.error_at("lvalue expected before `", "`");
}
}
void Expr::chk_type(const Lexem& lem) const {
if (!is_type()) {
lem.error_at("type expression expected before `", "`");
}
}
bool Expr::deduce_type(const Lexem& lem) {
if (e_type) {
return true;
}
switch (cls) {
case _Apply: {
if (!sym) {
return false;
}
SymVal* sym_val = dynamic_cast<SymVal*>(sym->value);
if (!sym_val || !sym_val->get_type()) {
return false;
}
std::vector<TypeExpr*> arg_types;
for (const auto& arg : args) {
arg_types.push_back(arg->e_type);
}
TypeExpr* fun_type = TypeExpr::new_map(TypeExpr::new_tensor(arg_types), TypeExpr::new_hole());
try {
unify(fun_type, sym_val->sym_type);
} catch (UnifyError& ue) {
std::ostringstream os;
os << "cannot apply function " << sym->name() << " : " << sym_val->get_type() << " to arguments of type "
<< fun_type->args[0] << ": " << ue;
lem.error(os.str());
}
e_type = fun_type->args[1];
TypeExpr::remove_indirect(e_type);
return true;
}
case _VarApply: {
assert(args.size() == 2);
TypeExpr* fun_type = TypeExpr::new_map(args[1]->e_type, TypeExpr::new_hole());
try {
unify(fun_type, args[0]->e_type);
} catch (UnifyError& ue) {
std::ostringstream os;
os << "cannot apply expression of type " << args[0]->e_type << " to an expression of type " << args[1]->e_type
<< ": " << ue;
lem.error(os.str());
}
e_type = fun_type->args[1];
TypeExpr::remove_indirect(e_type);
return true;
}
case _Letop: {
assert(args.size() == 2);
try {
// std::cerr << "in assignment: " << args[0]->e_type << " from " << args[1]->e_type << std::endl;
unify(args[0]->e_type, args[1]->e_type);
} catch (UnifyError& ue) {
std::ostringstream os;
os << "cannot assign an expression of type " << args[1]->e_type << " to a variable or pattern of type "
<< args[0]->e_type << ": " << ue;
lem.error(os.str());
}
e_type = args[0]->e_type;
TypeExpr::remove_indirect(e_type);
return true;
}
case _LetFirst: {
assert(args.size() == 2);
TypeExpr* rhs_type = TypeExpr::new_tensor({args[0]->e_type, TypeExpr::new_hole()});
try {
// std::cerr << "in implicit assignment of a modifying method: " << rhs_type << " and " << args[1]->e_type << std::endl;
unify(rhs_type, args[1]->e_type);
} catch (UnifyError& ue) {
std::ostringstream os;
os << "cannot implicitly assign an expression of type " << args[1]->e_type
<< " to a variable or pattern of type " << rhs_type << " in modifying method `" << sym::symbols.get_name(val)
<< "` : " << ue;
lem.error(os.str());
}
e_type = rhs_type->args[1];
TypeExpr::remove_indirect(e_type);
// std::cerr << "result type is " << e_type << std::endl;
return true;
}
case _CondExpr: {
assert(args.size() == 3);
auto flag_type = TypeExpr::new_atomic(_Int);
try {
unify(args[0]->e_type, flag_type);
} catch (UnifyError& ue) {
std::ostringstream os;
os << "condition in a conditional expression has non-integer type " << args[0]->e_type << ": " << ue;
lem.error(os.str());
}
try {
unify(args[1]->e_type, args[2]->e_type);
} catch (UnifyError& ue) {
std::ostringstream os;
os << "the two variants in a conditional expression have different types " << args[1]->e_type << " and "
<< args[2]->e_type << " : " << ue;
lem.error(os.str());
}
e_type = args[1]->e_type;
TypeExpr::remove_indirect(e_type);
return true;
}
}
return false;
}
int Expr::define_new_vars(CodeBlob& code) {
switch (cls) {
case _Tuple:
case _TypeApply: {
int res = 0;
for (const auto& x : args) {
res += x->define_new_vars(code);
}
return res;
}
case _Var:
if (val < 0) {
val = code.create_var(TmpVar::_Named, e_type, sym, &here);
return 1;
}
break;
case _Hole:
if (val < 0) {
val = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here);
}
break;
}
return 0;
}
int Expr::predefine_vars() {
switch (cls) {
case _Tuple:
case _TypeApply: {
int res = 0;
for (const auto& x : args) {
res += x->predefine_vars();
}
return res;
}
case _Var:
if (!sym) {
assert(val < 0 && here.defined());
sym = sym::define_symbol(~val, false, here);
if (!sym) {
throw src::ParseError{here, std::string{"redefined variable `"} + sym::symbols.get_name(~val) + "`"};
}
sym->value = new SymVal{SymVal::_Var, -1, e_type};
return 1;
}
break;
}
return 0;
}
std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code) const {
switch (cls) {
case _Tuple: {
std::vector<var_idx_t> res;
for (const auto& x : args) {
auto add = x->pre_compile(code);
res.insert(res.end(), add.cbegin(), add.cend());
}
return res;
}
case _Apply: {
assert(sym);
std::vector<var_idx_t> res;
auto func = dynamic_cast<SymValFunc*>(sym->value);
if (func && func->arg_order.size() == args.size()) {
//std::cerr << "!!! reordering " << args.size() << " arguments of " << sym->name() << std::endl;
std::vector<std::vector<var_idx_t>> add_list(args.size());
for (int i : func->arg_order) {
add_list[i] = args[i]->pre_compile(code);
}
for (const auto& add : add_list) {
res.insert(res.end(), add.cbegin(), add.cend());
}
} else {
for (const auto& x : args) {
auto add = x->pre_compile(code);
res.insert(res.end(), add.cbegin(), add.cend());
}
}
var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here);
std::vector<var_idx_t> rvect{rv};
auto& op = code.emplace_back(here, Op::_Call, rvect, std::move(res), sym);
if (flags & _IsImpure) {
op.flags |= Op::_Impure;
}
return rvect;
}
case _TypeApply:
return args[0]->pre_compile(code);
case _Var:
case _Hole:
return {val};
case _VarApply:
if (args[0]->cls == _Glob) {
std::vector<var_idx_t> res = args[1]->pre_compile(code);
var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here);
std::vector<var_idx_t> rvect{rv};
auto& op = code.emplace_back(here, Op::_Call, rvect, std::move(res), args[0]->sym);
if (args[0]->flags & _IsImpure) {
op.flags |= Op::_Impure;
}
return rvect;
} else {
std::vector<var_idx_t> res = args[1]->pre_compile(code);
std::vector<var_idx_t> tfunc = args[0]->pre_compile(code);
if (tfunc.size() != 1) {
throw src::Fatal{"stack tuple used as a function"};
}
res.push_back(tfunc[0]);
var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here);
std::vector<var_idx_t> rvect{rv};
code.emplace_back(here, Op::_CallInd, rvect, std::move(res));
return rvect;
}
case _Const: {
var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here);
std::vector<var_idx_t> rvect{rv};
code.emplace_back(here, Op::_IntConst, rvect, intval);
return rvect;
}
case _Glob: {
var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here);
std::vector<var_idx_t> rvect{rv};
code.emplace_back(here, Op::_GlobVar, rvect, std::vector<var_idx_t>{}, sym);
return rvect;
}
case _Letop: {
std::vector<var_idx_t> right = args[1]->pre_compile(code);
std::vector<var_idx_t> left = args[0]->pre_compile(code);
code.emplace_back(here, Op::_Let, left, std::move(right));
return left;
}
case _LetFirst: {
var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here);
std::vector<var_idx_t> right = args[1]->pre_compile(code);
std::vector<var_idx_t> left = args[0]->pre_compile(code);
left.push_back(rv);
code.emplace_back(here, Op::_Let, std::move(left), std::move(right));
return std::vector<var_idx_t>{rv};
}
case _CondExpr: {
auto cond = args[0]->pre_compile(code);
assert(cond.size() == 1);
var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here);
std::vector<var_idx_t> rvect{rv};
Op& if_op = code.emplace_back(here, Op::_If, cond);
code.push_set_cur(if_op.block0);
code.emplace_back(here, Op::_Let, rvect, args[1]->pre_compile(code));
code.close_pop_cur(args[1]->here);
code.push_set_cur(if_op.block1);
code.emplace_back(here, Op::_Let, rvect, args[2]->pre_compile(code));
code.close_pop_cur(args[2]->here);
return rvect;
}
default:
std::cerr << "expression constructor is " << cls << std::endl;
throw src::Fatal{"cannot compile expression with unknown constructor"};
}
}
} // namespace funC

115
crypto/func/keywords.cpp Normal file
View file

@ -0,0 +1,115 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "func.h"
namespace src {
int lexem_is_special(std::string str) {
return 0; // no special lexems
}
} // namespace src
namespace funC {
/*
*
* KEYWORD DEFINITION
*
*/
void define_keywords() {
sym::symbols.add_kw_char('+')
.add_kw_char('-')
.add_kw_char('*')
.add_kw_char('/')
.add_kw_char('%')
.add_kw_char('?')
.add_kw_char(':')
.add_kw_char(',')
.add_kw_char(';')
.add_kw_char('(')
.add_kw_char(')')
.add_kw_char('{')
.add_kw_char('}')
.add_kw_char('=')
.add_kw_char('_')
.add_kw_char('<')
.add_kw_char('>')
.add_kw_char('&')
.add_kw_char('|')
.add_kw_char('~');
using Kw = funC::Keyword;
sym::symbols.add_keyword("==", Kw::_Eq)
.add_keyword("!=", Kw::_Neq)
.add_keyword("<=", Kw::_Leq)
.add_keyword(">=", Kw::_Geq)
.add_keyword("<=>", Kw::_Spaceship)
.add_keyword("<<", Kw::_Lshift)
.add_keyword(">>", Kw::_Rshift)
.add_keyword(">>~", Kw::_RshiftR)
.add_keyword(">>^", Kw::_RshiftC)
.add_keyword("/~", Kw::_DivR)
.add_keyword("/^", Kw::_DivC)
.add_keyword("/%", Kw::_DivMod)
.add_keyword("+=", Kw::_PlusLet)
.add_keyword("-=", Kw::_MinusLet)
.add_keyword("*=", Kw::_TimesLet)
.add_keyword("/=", Kw::_DivLet)
.add_keyword("%=", Kw::_ModLet)
.add_keyword("/~=", Kw::_DivRLet)
.add_keyword("/^=", Kw::_DivCLet)
.add_keyword("<<=", Kw::_LshiftLet)
.add_keyword(">>=", Kw::_RshiftLet)
.add_keyword(">>~=", Kw::_RshiftRLet)
.add_keyword(">>^=", Kw::_RshiftCLet);
sym::symbols.add_keyword("return", Kw::_Return)
.add_keyword("var", Kw::_Var)
.add_keyword("repeat", Kw::_Repeat)
.add_keyword("do", Kw::_Do)
.add_keyword("while", Kw::_While)
.add_keyword("until", Kw::_Until)
.add_keyword("if", Kw::_If)
.add_keyword("ifnot", Kw::_Ifnot)
.add_keyword("then", Kw::_Then)
.add_keyword("else", Kw::_Else)
.add_keyword("elseif", Kw::_Elseif)
.add_keyword("elseifnot", Kw::_Elseifnot);
sym::symbols.add_keyword("int", Kw::_Int)
.add_keyword("cell", Kw::_Cell)
.add_keyword("slice", Kw::_Slice)
.add_keyword("builder", Kw::_Builder)
.add_keyword("cont", Kw::_Cont)
.add_keyword("tuple", Kw::_Tuple)
.add_keyword("->", Kw::_Mapsto);
sym::symbols.add_keyword("extern", Kw::_Extern)
.add_keyword("asm", Kw::_Asm)
.add_keyword("impure", Kw::_Impure)
.add_keyword("method_id", Kw::_MethodId)
.add_keyword("operator", Kw::_Operator)
.add_keyword("infix", Kw::_Infix)
.add_keyword("infixl", Kw::_Infixl)
.add_keyword("infixr", Kw::_Infixr);
}
} // namespace funC

539
crypto/func/optimize.cpp Normal file
View file

@ -0,0 +1,539 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "func.h"
namespace funC {
/*
*
* PEEPHOLE OPTIMIZER
*
*/
void Optimizer::set_code(AsmOpConsList code) {
code_ = std::move(code);
unpack();
}
void Optimizer::unpack() {
int i = 0, j = 0;
for (AsmOpCons *p = code_.get(); p && i < n; p = p->cdr.get(), ++j) {
if (p->car->is_very_custom()) {
break;
}
if (p->car->is_comment()) {
continue;
}
op_cons_[i] = p;
op_[i] = std::move(p->car);
offs_[i] = j;
++i;
}
l_ = i;
indent_ = (i ? op_[0]->indent : 0);
}
void Optimizer::pack() {
for (int i = 0; i < l_; i++) {
op_cons_[i]->car = std::move(op_[i]);
op_cons_[i] = nullptr;
}
l_ = 0;
}
void Optimizer::apply() {
if (!p_ && !q_) {
return;
}
assert(p_ > 0 && p_ <= l_ && q_ >= 0 && q_ <= n && l_ <= n);
for (int i = p_; i < l_; i++) {
assert(op_[i]);
op_cons_[i]->car = std::move(op_[i]);
op_cons_[i] = nullptr;
}
for (int c = offs_[p_ - 1]; c >= 0; --c) {
code_ = std::move(code_->cdr);
}
for (int j = q_ - 1; j >= 0; j--) {
assert(oq_[j]);
oq_[j]->indent = indent_;
code_ = AsmOpCons::cons(std::move(oq_[j]), std::move(code_));
}
l_ = 0;
}
AsmOpConsList Optimizer::extract_code() {
pack();
return std::move(code_);
}
void Optimizer::show_head() const {
if (!debug_) {
return;
}
std::cerr << "optimizing";
for (int i = 0; i < l_; i++) {
if (op_[i]) {
std::cerr << ' ' << *op_[i] << ' ';
} else {
std::cerr << " (null) ";
}
}
std::cerr << std::endl;
}
void Optimizer::show_left() const {
if (!debug_) {
return;
}
std::cerr << "// *** rewriting";
for (int i = 0; i < p_; i++) {
if (op_[i]) {
std::cerr << ' ' << *op_[i] << ' ';
} else {
std::cerr << " (null) ";
}
}
}
void Optimizer::show_right() const {
if (!debug_) {
return;
}
std::cerr << "->";
for (int i = 0; i < q_; i++) {
if (oq_[i]) {
std::cerr << ' ' << *oq_[i] << ' ';
} else {
std::cerr << " (null) ";
}
}
std::cerr << std::endl;
}
bool Optimizer::say(std::string str) const {
if (debug_) {
std::cerr << str << std::endl;
}
return true;
}
bool Optimizer::is_const_push_swap() const {
return l_ >= 3 && op_[0]->is_gconst() && op_[1]->is_push() && op_[1]->a >= 1 && op_[2]->is_swap();
}
// PUSHCONST c ; PUSH s(i+1) ; SWAP -> PUSH s(i) ; PUSHCONST c
bool Optimizer::rewrite_const_push_swap() {
p_ = 3;
q_ = 2;
show_left();
oq_[1] = std::move(op_[0]);
oq_[0] = std::move(op_[1]);
(oq_[0]->a)--;
show_right();
return true;
}
bool Optimizer::is_const_push_xchgs() {
if (!(pb_ >= 2 && pb_ <= l2_ && op_[0]->is_gconst())) {
return false;
}
StackTransform t;
int pos = 0, i;
for (i = 1; i < pb_; i++) {
int a, b;
if (op_[i]->is_xchg(&a, &b)) {
if (pos == a) {
pos = b;
} else if (pos == b) {
pos = a;
} else {
t.apply_xchg(a - (a > pos), b - (b > pos));
}
} else if (op_[i]->is_push(&a)) {
if (pos == a) {
return false;
}
t.apply_push(a - (a > pos));
++pos;
} else {
return false;
}
}
if (pos) {
return false;
}
t.apply_push_newconst();
if (t <= tr_[i - 1]) {
p_ = i;
return true;
} else {
return false;
}
}
bool Optimizer::rewrite_const_push_xchgs() {
if (!p_) {
return false;
}
show_left();
auto c_op = std::move(op_[0]);
assert(c_op->is_gconst());
StackTransform t;
q_ = 0;
int pos = 0;
for (int i = 1; i < p_; i++) {
int a, b;
if (op_[i]->is_xchg(&a, &b)) {
if (a == pos) {
pos = b;
} else if (b == pos) {
pos = a;
} else {
oq_[q_] = std::move(op_[i]);
if (a > pos) {
oq_[q_]->a = a - 1;
}
if (b > pos) {
oq_[q_]->b = b - 1;
}
assert(apply_op(t, *oq_[q_]));
++q_;
}
} else {
assert(op_[i]->is_push(&a));
assert(a != pos);
oq_[q_] = std::move(op_[i]);
if (a > pos) {
oq_[q_]->a = a - 1;
}
assert(apply_op(t, *oq_[q_]));
++q_;
++pos;
}
}
assert(!pos);
t.apply_push_newconst();
assert(t <= tr_[p_ - 1]);
oq_[q_++] = std::move(c_op);
show_right();
return true;
}
bool Optimizer::simple_rewrite(int p, AsmOp&& new_op) {
assert(p > 0 && p <= l_);
p_ = p;
q_ = 1;
show_left();
oq_[0] = std::move(op_[0]);
*oq_[0] = new_op;
show_right();
return true;
}
bool Optimizer::simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2) {
assert(p > 1 && p <= l_);
p_ = p;
q_ = 2;
show_left();
oq_[0] = std::move(op_[0]);
*oq_[0] = new_op1;
oq_[1] = std::move(op_[1]);
*oq_[1] = new_op2;
show_right();
return true;
}
bool Optimizer::simple_rewrite_nop() {
assert(p_ > 0 && p_ <= l_);
q_ = 0;
show_left();
show_right();
return true;
}
bool Optimizer::is_pred(const std::function<bool(const StackTransform&)>& pred, int min_p) {
min_p = std::max(min_p, pb_);
for (int p = l2_; p >= min_p; p--) {
if (pred(tr_[p - 1])) {
p_ = p;
return true;
}
}
return false;
}
bool Optimizer::is_same_as(const StackTransform& trans, int min_p) {
return is_pred([&trans](const auto& t) { return t >= trans; }, min_p);
}
// s1 s3 XCHG ; s0 s2 XCHG -> 2SWAP
bool Optimizer::is_2swap() {
static const StackTransform t_2swap{2, 3, 0, 1, 4};
return is_same_as(t_2swap);
}
// s3 PUSH ; s3 PUSH -> 2OVER
bool Optimizer::is_2over() {
static const StackTransform t_2over{2, 3, 0};
return is_same_as(t_2over);
}
bool Optimizer::is_2dup() {
static const StackTransform t_2dup{0, 1, 0};
return is_same_as(t_2dup);
}
bool Optimizer::is_tuck() {
static const StackTransform t_tuck{0, 1, 0, 2};
return is_same_as(t_tuck);
}
bool Optimizer::is_2drop() {
static const StackTransform t_2drop{2};
return is_same_as(t_2drop);
}
bool Optimizer::is_rot() {
return is_pred([](const auto& t) { return t.is_rot(); });
}
bool Optimizer::is_rotrev() {
return is_pred([](const auto& t) { return t.is_rotrev(); });
}
bool Optimizer::is_nop() {
return is_pred([](const auto& t) { return t.is_id(); }, 1);
}
bool Optimizer::is_xchg(int* i, int* j) {
return is_pred([i, j](const auto& t) { return t.is_xchg(i, j) && ((*i < 16 && *j < 16) || (!*i && *j < 256)); });
}
bool Optimizer::is_push(int* i) {
return is_pred([i](const auto& t) { return t.is_push(i) && *i < 256; });
}
bool Optimizer::is_pop(int* i) {
return is_pred([i](const auto& t) { return t.is_pop(i) && *i < 256; });
}
bool Optimizer::is_xchg2(int* i, int* j) {
return is_pred([i, j](const auto& t) { return t.is_xchg2(i, j) && *i < 16 && *j < 16; });
}
bool Optimizer::is_xcpu(int* i, int* j) {
return is_pred([i, j](const auto& t) { return t.is_xcpu(i, j) && *i < 16 && *j < 16; });
}
bool Optimizer::is_puxc(int* i, int* j) {
return is_pred([i, j](const auto& t) { return t.is_puxc(i, j) && *i < 16 && *j < 15; });
}
bool Optimizer::is_push2(int* i, int* j) {
return is_pred([i, j](const auto& t) { return t.is_push2(i, j) && *i < 16 && *j < 16; });
}
bool Optimizer::is_xchg3(int* i, int* j, int* k) {
return is_pred([i, j, k](const auto& t) { return t.is_xchg3(i, j, k) && *i < 16 && *j < 16 && *k < 16; });
}
bool Optimizer::is_xc2pu(int* i, int* j, int* k) {
return is_pred([i, j, k](const auto& t) { return t.is_xc2pu(i, j, k) && *i < 16 && *j < 16 && *k < 16; });
}
bool Optimizer::is_xcpuxc(int* i, int* j, int* k) {
return is_pred([i, j, k](const auto& t) { return t.is_xcpuxc(i, j, k) && *i < 16 && *j < 16 && *k < 15; });
}
bool Optimizer::is_xcpu2(int* i, int* j, int* k) {
return is_pred([i, j, k](const auto& t) { return t.is_xcpu2(i, j, k) && *i < 16 && *j < 16 && *k < 16; });
}
bool Optimizer::is_puxc2(int* i, int* j, int* k) {
return is_pred([i, j, k](const auto& t) { return t.is_puxc2(i, j, k) && *i < 16 && *j < 15 && *k < 15; });
}
bool Optimizer::is_puxcpu(int* i, int* j, int* k) {
return is_pred([i, j, k](const auto& t) { return t.is_puxcpu(i, j, k) && *i < 16 && *j < 15 && *k < 15; });
}
bool Optimizer::is_pu2xc(int* i, int* j, int* k) {
return is_pred([i, j, k](const auto& t) { return t.is_pu2xc(i, j, k) && *i < 16 && *j < 15 && *k < 14; });
}
bool Optimizer::is_push3(int* i, int* j, int* k) {
return is_pred([i, j, k](const auto& t) { return t.is_push3(i, j, k) && *i < 16 && *j < 16 && *k < 16; });
}
bool Optimizer::is_blkswap(int* i, int* j) {
return is_pred([i, j](const auto& t) { return t.is_blkswap(i, j) && *i > 0 && *j > 0 && *i <= 16 && *j <= 16; });
}
bool Optimizer::is_blkpush(int* i, int* j) {
return is_pred([i, j](const auto& t) { return t.is_blkpush(i, j) && *i > 0 && *i < 16 && *j < 16; });
}
bool Optimizer::is_blkdrop(int* i) {
return is_pred([i](const auto& t) { return t.is_blkdrop(i) && *i > 0 && *i < 16; });
}
bool Optimizer::is_reverse(int* i, int* j) {
return is_pred([i, j](const auto& t) { return t.is_reverse(i, j) && *i >= 2 && *i <= 17 && *j < 16; });
}
bool Optimizer::is_nip_seq(int* i, int* j) {
return is_pred([i, j](const auto& t) { return t.is_nip_seq(i, j) && *i >= 3 && *i <= 15; });
}
bool Optimizer::compute_stack_transforms() {
StackTransform trans;
for (int i = 0; i < l_; i++) {
if (!apply_op(trans, *op_[i])) {
l2_ = i;
return true;
}
tr_[i] = trans;
}
l2_ = l_;
return true;
}
bool Optimizer::show_stack_transforms() const {
show_head();
// slow version
/*
StackTransform trans2;
std::cerr << "id = " << trans2 << std::endl;
for (int i = 0; i < l_; i++) {
StackTransform op;
if (!apply_op(op, *op_[i])) {
std::cerr << "* (" << *op_[i] << " = invalid)\n";
break;
}
trans2 *= op;
std::cerr << "* " << *op_[i] << " = " << op << " -> " << trans2 << std::endl;
}
*/
// fast version
StackTransform trans;
for (int i = 0; i < l_; i++) {
std::cerr << trans << std::endl << *op_[i] << " -> ";
if (!apply_op(trans, *op_[i])) {
std::cerr << " <not-applicable>" << std::endl;
return true;
}
}
std::cerr << trans << std::endl;
return true;
}
bool Optimizer::find_at_least(int pb) {
p_ = q_ = 0;
pb_ = pb;
// show_stack_transforms();
int i = -100, j = -100, k = -100;
return (is_const_push_swap() && 3 >= pb && rewrite_const_push_swap()) || (is_nop() && simple_rewrite_nop()) ||
(is_const_push_xchgs() && rewrite_const_push_xchgs()) ||
(is_xchg(&i, &j) && simple_rewrite(AsmOp::Xchg(i, j))) || (is_push(&i) && simple_rewrite(AsmOp::Push(i))) ||
(is_pop(&i) && simple_rewrite(AsmOp::Pop(i))) || (is_rot() && simple_rewrite(AsmOp::Custom("ROT", 3, 3))) ||
(is_rotrev() && simple_rewrite(AsmOp::Custom("-ROT", 3, 3))) ||
(is_2dup() && simple_rewrite(AsmOp::Custom("2DUP", 2, 4))) ||
(is_2swap() && simple_rewrite(AsmOp::Custom("2SWAP", 2, 4))) ||
(is_2over() && simple_rewrite(AsmOp::Custom("2OVER", 2, 4))) ||
(is_tuck() && simple_rewrite(AsmOp::Custom("TUCK", 2, 3))) ||
(is_2drop() && simple_rewrite(AsmOp::Custom("2DROP", 2, 0))) ||
(is_xchg2(&i, &j) && simple_rewrite(AsmOp::Xchg2(i, j))) ||
(is_xcpu(&i, &j) && simple_rewrite(AsmOp::XcPu(i, j))) ||
(is_puxc(&i, &j) && simple_rewrite(AsmOp::PuXc(i, j))) ||
(is_push2(&i, &j) && simple_rewrite(AsmOp::Push2(i, j))) ||
(is_blkswap(&i, &j) && simple_rewrite(AsmOp::BlkSwap(i, j))) ||
(is_blkpush(&i, &j) && simple_rewrite(AsmOp::BlkPush(i, j))) ||
(is_blkdrop(&i) && simple_rewrite(AsmOp::BlkDrop(i))) ||
(is_reverse(&i, &j) && simple_rewrite(AsmOp::BlkReverse(i, j))) ||
(is_nip_seq(&i, &j) && simple_rewrite(AsmOp::Xchg(i, j), AsmOp::BlkDrop(i))) ||
(is_xchg3(&i, &j, &k) && simple_rewrite(AsmOp::Xchg3(i, j, k))) ||
(is_xc2pu(&i, &j, &k) && simple_rewrite(AsmOp::Xc2Pu(i, j, k))) ||
(is_xcpuxc(&i, &j, &k) && simple_rewrite(AsmOp::XcPuXc(i, j, k))) ||
(is_xcpu2(&i, &j, &k) && simple_rewrite(AsmOp::XcPu2(i, j, k))) ||
(is_puxc2(&i, &j, &k) && simple_rewrite(AsmOp::PuXc2(i, j, k))) ||
(is_puxcpu(&i, &j, &k) && simple_rewrite(AsmOp::PuXcPu(i, j, k))) ||
(is_pu2xc(&i, &j, &k) && simple_rewrite(AsmOp::Pu2Xc(i, j, k))) ||
(is_push3(&i, &j, &k) && simple_rewrite(AsmOp::Push3(i, j, k)));
}
bool Optimizer::find() {
if (!compute_stack_transforms()) {
return false;
}
for (int pb = l_; pb > 0; --pb) {
if (find_at_least(pb)) {
return true;
}
}
return false;
}
bool Optimizer::optimize() {
bool f = false;
while (find()) {
f = true;
apply();
unpack();
}
return f;
}
AsmOpConsList optimize_code_head(AsmOpConsList op_list) {
Optimizer opt(std::move(op_list), op_rewrite_comments);
opt.optimize();
return opt.extract_code();
}
AsmOpConsList optimize_code(AsmOpConsList op_list) {
std::vector<std::unique_ptr<AsmOp>> v;
while (op_list) {
if (!op_list->car->is_comment()) {
op_list = optimize_code_head(std::move(op_list));
}
if (op_list) {
v.push_back(std::move(op_list->car));
op_list = std::move(op_list->cdr);
}
}
for (auto it = v.rbegin(); it < v.rend(); ++it) {
op_list = AsmOpCons::cons(std::move(*it), std::move(op_list));
}
return std::move(op_list);
}
void optimize_code(AsmOpList& ops) {
std::unique_ptr<AsmOpCons> op_list;
for (auto it = ops.list_.rbegin(); it < ops.list_.rend(); ++it) {
op_list = AsmOpCons::cons(std::make_unique<AsmOp>(std::move(*it)), std::move(op_list));
}
op_list = optimize_code(std::move(op_list));
ops.list_.clear();
while (op_list) {
ops.list_.push_back(std::move(*(op_list->car)));
op_list = std::move(op_list->cdr);
}
}
} // namespace funC

1105
crypto/func/parse-func.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,826 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "func.h"
namespace funC {
/*
*
* GENERIC STACK TRANSFORMATIONS
*
*/
StackTransform::StackTransform(std::initializer_list<int> list) {
*this = list;
}
StackTransform &StackTransform::operator=(std::initializer_list<int> list) {
if (list.size() > 255) {
invalidate();
return *this;
}
set_id();
if (!list.size()) {
return *this;
}
int m = (int)list.size();
d = list.begin()[m - 1] - (m - 1);
if (d >= 128 || d < -128) {
invalidate();
return *this;
}
for (int i = 0; i < m - 1; i++) {
int x = d + i;
int y = list.begin()[i];
if (y != x) {
if (x != (short)x || y != (short)y || n == max_n) {
invalidate();
return *this;
}
dp = std::max(dp, std::max(x, y) + 1);
A[n++] = std::make_pair((short)x, (short)y);
}
}
return *this;
}
bool StackTransform::assign(const StackTransform &other) {
if (!other.is_valid() || (unsigned)other.n > max_n) {
return invalidate();
}
d = other.d;
n = other.n;
dp = other.dp;
c = other.c;
invalid = false;
for (int i = 0; i < n; i++) {
A[i] = other.A[i];
}
return true;
}
int StackTransform::get(int x) const {
if (!is_valid()) {
return -1;
}
if (x <= c_start) {
return x - c;
}
x += d;
int i;
for (i = 0; i < n && A[i].first < x; i++) {
}
if (i < n && A[i].first == x) {
return A[i].second;
} else {
return x;
}
}
bool StackTransform::set(int x, int y, bool relaxed) {
if (!is_valid()) {
return false;
}
if (x < 0) {
return (relaxed && y == x + d) || invalidate();
}
if (!relaxed) {
touch(x);
}
x += d;
int i;
for (i = 0; i < n && A[i].first < x; i++) {
}
if (i < n && A[i].first == x) {
if (x != y) {
if (y != (short)y) {
return invalidate();
}
A[i].second = (short)y;
} else {
--n;
for (; i < n; i++) {
A[i] = A[i + 1];
}
}
} else {
if (x != y) {
if (x != (short)x || y != (short)y || n == max_n) {
return invalidate();
}
for (int j = n++; j > i; j--) {
A[j] = A[j - 1];
}
A[i].first = (short)x;
A[i].second = (short)y;
touch(x - d);
touch(y);
}
}
return true;
}
// f(x') = x' + d for all x' >= x ?
bool StackTransform::is_trivial_after(int x) const {
return is_valid() && (!n || A[n - 1].first < x + d);
}
// card f^{-1}(y)
int StackTransform::preimage_count(int y) const {
if (!is_valid()) {
return -1;
}
int count = (y >= d);
for (const auto &pair : A) {
if (pair.second == y) {
++count;
} else if (pair.first == y) {
--count;
}
}
return count;
}
// f^{-1}(y)
std::vector<int> StackTransform::preimage(int y) const {
if (!is_valid()) {
return {};
}
std::vector<int> res;
bool f = (y >= d);
for (const auto &pair : A) {
if (pair.first > y && f) {
res.push_back(y - d);
f = false;
}
if (pair.first == y) {
f = false;
} else if (pair.second == y) {
res.push_back(pair.first - d);
}
}
return res;
}
// is f:N->N bijective ?
bool StackTransform::is_permutation() const {
if (!is_valid() || d) {
return false;
}
assert(n <= max_n);
std::array<int, max_n> X, Y;
for (int i = 0; i < n; i++) {
X[i] = A[i].first;
Y[i] = A[i].second;
if (Y[i] < 0) {
return false;
}
}
std::sort(Y.begin(), Y.begin() + n);
for (int i = 0; i < n; i++) {
if (X[i] != Y[i]) {
return false;
}
}
return true;
}
bool StackTransform::remove_negative() {
int s = 0;
while (s < n && A[s].first < d) {
++s;
}
if (s) {
n -= s;
for (int i = 0; i < n; i++) {
A[i] = A[i + s];
}
}
return true;
}
int StackTransform::try_load(int &i, int offs) const {
return i < n ? A[i++].first + offs : inf_x;
}
bool StackTransform::try_store(int x, int y) {
if (x == y || x < d) {
return true;
}
if (n == max_n || x != (short)x || y != (short)y) {
return invalidate();
}
A[n].first = (short)x;
A[n++].second = (short)y;
return true;
}
// c := a * b
bool StackTransform::compose(const StackTransform &a, const StackTransform &b, StackTransform &c) {
if (!a.is_valid() || !b.is_valid()) {
return c.invalidate();
}
c.d = a.d + b.d;
c.n = 0;
c.dp = std::max(a.dp, b.dp + a.d);
c.c = a.c + b.c;
c.invalid = false;
int i = 0, j = 0;
int x1 = a.try_load(i);
int x2 = b.try_load(j, a.d);
while (true) {
if (x1 < x2) {
int y = a.A[i - 1].second;
if (!c.try_store(x1, y)) {
return false;
}
x1 = a.try_load(i);
} else if (x2 < inf_x) {
if (x1 == x2) {
x1 = a.try_load(i);
}
int y = b.A[j - 1].second;
if (!c.try_store(x2, a(y))) {
return false;
}
x2 = b.try_load(j, a.d);
} else {
return true;
}
}
}
// this = this * other
bool StackTransform::apply(const StackTransform &other) {
StackTransform res;
if (!compose(*this, other, res)) {
return invalidate();
}
return assign(res);
}
// this = other * this
bool StackTransform::preapply(const StackTransform &other) {
StackTransform res;
if (!compose(other, *this, res)) {
return invalidate();
}
return assign(res);
}
StackTransform StackTransform::operator*(const StackTransform &b) const & {
StackTransform res;
compose(*this, b, res);
return res;
}
// this = this * other
StackTransform &StackTransform::operator*=(const StackTransform &other) {
StackTransform res;
(compose(*this, other, res) && assign(res)) || invalidate();
return *this;
}
bool StackTransform::apply_xchg(int i, int j, bool relaxed) {
if (!is_valid() || i < 0 || j < 0) {
return invalidate();
}
if (i == j) {
return relaxed || touch(i);
}
int u = touch_get(i), v = touch_get(j);
return set(i, v) && set(j, u);
}
bool StackTransform::apply_push(int i) {
if (!is_valid() || i < 0) {
return invalidate();
}
int u = touch_get(i);
return shift(-1) && set(0, u);
}
bool StackTransform::apply_push_newconst() {
if (!is_valid()) {
return false;
}
return shift(-1) && set(0, c_start - c++);
}
bool StackTransform::apply_pop(int i) {
if (!is_valid() || i < 0) {
return invalidate();
}
if (!i) {
return touch(0) && shift(1);
} else {
return set(i, get(0)) && shift(1);
}
}
bool StackTransform::equal(const StackTransform &other, bool relaxed) const {
if (!is_valid() || !other.is_valid()) {
return false;
}
if (!(n == other.n && d == other.d)) {
return false;
}
for (int i = 0; i < n; i++) {
if (A[i] != other.A[i]) {
return false;
}
}
return relaxed || dp == other.dp;
}
StackTransform StackTransform::Xchg(int i, int j, bool relaxed) {
StackTransform t;
t.apply_xchg(i, j, relaxed);
return t;
}
StackTransform StackTransform::Push(int i) {
StackTransform t;
t.apply_push(i);
return t;
}
StackTransform StackTransform::Pop(int i) {
StackTransform t;
t.apply_pop(i);
return t;
}
bool StackTransform::is_xchg(int i, int j) const {
if (i == j) {
return is_id();
}
return is_valid() && !d && n == 2 && i >= 0 && j >= 0 && get(i) == j && get(j) == i;
}
bool StackTransform::is_xchg(int *i, int *j) const {
if (!is_valid() || d || n > 2 || !dp) {
return false;
}
if (!n) {
*i = *j = 0;
return true;
}
if (n != 2) {
return false;
}
int a = A[0].first, b = A[1].first;
if (A[0].second != b || A[1].second != a) {
return false;
}
*i = std::min(a, b);
*j = std::max(a, b);
return true;
}
bool StackTransform::is_push(int i) const {
return is_valid() && d == -1 && n == 1 && A[0].first == -1 && A[0].second == i;
}
bool StackTransform::is_push(int *i) const {
if (is_valid() && d == -1 && n == 1 && A[0].first == -1 && A[0].second >= 0) {
*i = A[0].second;
return true;
} else {
return false;
}
}
// 1 2 3 4 .. = pop0
// 0 2 3 4 .. = pop1
// 1 0 3 4 .. = pop2
// 1 2 0 4 .. = pop3
bool StackTransform::is_pop(int i) const {
if (!is_valid() || d != 1 || n > 1 || i < 0) {
return false;
}
if (!i) {
return !n;
}
return n == 1 && A[0].first == i && !A[0].second;
}
bool StackTransform::is_pop(int *i) const {
if (!is_valid() || d != 1 || n > 1) {
return false;
}
if (!n) {
*i = 0;
return true;
}
if (n == 1 && !A[0].second) {
*i = A[0].first;
return true;
}
return false;
}
const StackTransform StackTransform::rot{2, 0, 1, 3};
const StackTransform StackTransform::rot_rev{1, 2, 0, 3};
bool StackTransform::is_rot() const {
return equal(rot, true);
}
bool StackTransform::is_rotrev() const {
return equal(rot_rev, true);
}
// XCHG s1,s(i) ; XCHG s0,s(j)
bool StackTransform::is_xchg2(int i, int j) const {
StackTransform t;
return is_valid() && !d && t.apply_xchg(1, i) && t.apply_xchg(0, j) && t <= *this;
}
bool StackTransform::is_xchg2(int *i, int *j) const {
if (!is_valid() || d || n > 4 || n == 1 || dp < 2) {
return false;
}
*i = get(1);
*j = get(0);
if (!n) {
return true;
}
if (*i < 0 || *j < 0) {
return false;
}
if (n != 3) {
return is_xchg2(*i, *j);
}
if (*i) {
// XCHG2 s(i),s(i) = XCHG s1,s(i) ; XCHG s0,s(i) : 0->1, 1->i
*j = *i;
} // XCHG2 s0,s(i) = XCHG s0,s1 ; XCHG s0,s(i) : 0->i, 1->0
return is_xchg2(*i, *j);
}
// XCHG s0,s(i) ; PUSH s(j) = PUSH s(j') ; XCHG s1,s(i+1)
// j'=j if j!=0, j!=i
// j'=0 if j=i
// j'=i if j=0
bool StackTransform::is_xcpu(int i, int j) const {
StackTransform t;
return is_valid() && d == -1 && t.apply_xchg(0, i) && t.apply_push(j) && t <= *this;
}
bool StackTransform::is_xcpu(int *i, int *j) const {
if (!is_valid() || d != -1 || n > 3 || dp < 1) {
return false;
}
*i = get(1);
*j = get(0);
if (!*j) {
*j = *i;
} else if (*j == *i) {
*j = 0;
}
return is_xcpu(*i, *j);
}
// PUSH s(i) ; XCHG s0, s1 ; XCHG s0, s(j+1)
bool StackTransform::is_puxc(int i, int j) const {
StackTransform t;
return is_valid() && d == -1 && t.apply_push(i) && t.apply_xchg(0, 1) && t.apply_xchg(0, j + 1) && t <= *this;
}
// j > 0 : 0 -> j, 1 -> i
// j = 0 : 0 -> i, 1 -> 0 ( PUSH s(i) )
// j = -1 : 0 -> 0, 1 -> i ( PUSH s(i) ; XCHG s0, s1 )
bool StackTransform::is_puxc(int *i, int *j) const {
if (!is_valid() || d != -1 || n > 3) {
return false;
}
*i = get(1);
*j = get(0);
if (!*i && is_push(*j)) {
std::swap(*i, *j);
return is_puxc(*i, *j);
}
if (!*j) {
--*j;
}
return is_puxc(*i, *j);
}
// PUSH s(i) ; PUSH s(j+1)
bool StackTransform::is_push2(int i, int j) const {
StackTransform t;
return is_valid() && d == -2 && t.apply_push(i) && t.apply_push(j + 1) && t <= *this;
}
bool StackTransform::is_push2(int *i, int *j) const {
if (!is_valid() || d != -2 || n > 2) {
return false;
}
*i = get(1);
*j = get(0);
return is_push2(*i, *j);
}
// XCHG s2,s(i) ; XCHG s1,s(j) ; XCHG s0,s(k)
bool StackTransform::is_xchg3(int *i, int *j, int *k) const {
if (!is_valid() || d || dp < 3 || !is_permutation()) {
return false;
}
for (int s = 2; s >= 0; s--) {
*i = get(s);
StackTransform t = Xchg(2, *i) * *this;
if (t.is_xchg2(j, k)) {
return true;
}
}
return false;
}
// XCHG s1,s(i) ; XCHG s0,s(j) ; PUSH s(k)
bool StackTransform::is_xc2pu(int *i, int *j, int *k) const {
if (!is_valid() || d != -1 || dp < 2) {
return false;
}
for (int s = 2; s >= 1; s--) {
*i = get(s);
StackTransform t = Xchg(1, *i) * *this;
if (t.is_xcpu(j, k)) {
return true;
}
}
return false;
}
// XCHG s1,s(i) ; PUSH s(j) ; XCHG s0,s1 ; XCHG s0,s(k+1)
bool StackTransform::is_xcpuxc(int *i, int *j, int *k) const {
if (!is_valid() || d != -1 || dp < 2) {
return false;
}
for (int s = 2; s >= 0; s--) {
*i = get(s);
StackTransform t = Xchg(1, *i) * *this;
if (t.is_puxc(j, k)) {
return true;
}
}
return false;
}
// XCHG s0,s(i) ; PUSH s(j) ; PUSH s(k+1)
bool StackTransform::is_xcpu2(int *i, int *j, int *k) const {
if (!is_valid() || d != -2 || dp < 1) {
return false;
}
*i = get(2);
StackTransform t = Xchg(0, *i) * *this;
return t.is_push2(j, k);
}
// PUSH s(i) ; XCHG s0,s2 ; XCHG s1,s(j+1) ; XCHG s0,s(k+1)
// 0 -> i or 1 -> i or 2 -> i ; i has two preimages
// 0 -> k if k >= 2, k != j
// 1 -> j=k if j = k >= 2
// 1 -> j if j >= 2, k != 0
// 0 -> j if j >= 2, k = 0
// => i in {f(0), f(1), f(2)} ; j in {-1, 0, 1, f(0), f(1)} ; k in {-1, 0, 1, f(0), f(1)}
bool StackTransform::is_puxc2(int *i, int *j, int *k) const {
if (!is_valid() || d != -1 || dp < 2) {
return false;
}
for (int s = 2; s >= 0; s--) {
*i = get(s);
if (preimage_count(*i) != 2) {
continue;
}
for (int u = -1; u <= 3; u++) {
*j = (u >= 2 ? get(u - 2) : u);
for (int v = -1; v <= 3; v++) {
*k = (v >= 2 ? get(v - 2) : v);
if (is_puxc2(*i, *j, *k)) {
return true;
}
}
}
}
return false;
}
// PUSH s(i) ; XCHG s0,s2 ; XCHG s1,s(j+1) ; XCHG s0,s(k+1)
bool StackTransform::is_puxc2(int i, int j, int k) const {
StackTransform t;
return is_valid() && d == -1 && dp >= 2 // basic checks
&& t.apply_push(i) && t.apply_xchg(0, 2) // PUSH s(i) ; XCHG s0,s2
&& t.apply_xchg(1, j + 1) // XCHG s1,s(j+1)
&& t.apply_xchg(0, k + 1) && t <= *this; // XCHG s0,s(k+2)
}
// PUSH s(i) ; XCHG s0,s1 ; XCHG s0,s(j+1) ; PUSH s(k+1)
bool StackTransform::is_puxcpu(int *i, int *j, int *k) const {
if (!is_valid() || d != -2 || dp < 1) {
return false;
}
StackTransform t = *this;
if (t.apply_pop() && t.is_puxc(i, j)) {
int y = get(0);
auto v = t.preimage(y);
if (!v.empty()) {
*k = v[0] - 1;
t.apply_push(*k + 1);
return t <= *this;
}
}
return false;
}
// PUSH s(i) ; XCHG s0,s1 ; PUSH s(j+1) ; XCHG s0,s1 ; XCHG s0,s(k+2)
// 2 -> i; 1 -> j (if j >= 1, k != -1), 1 -> i (if j = 0, k != -1), 1 -> 0 (if j = -1, k != -1)
// 0 -> k (if k >= 1), 0 -> i (if k = 0), 0 -> j (if k = -1, j >= 1)
bool StackTransform::is_pu2xc(int *i, int *j, int *k) const {
if (!is_valid() || d != -2 || dp < 1) {
return false;
}
*i = get(2);
for (int v = -2; v <= 1; v++) {
*k = (v <= 0 ? v : get(0)); // one of -2, -1, 0, get(0)
for (int u = -1; u <= 1; u++) {
*j = (u <= 0 ? u : get(v != -1)); // one of -1, 0, get(0), get(1)
if (is_pu2xc(*i, *j, *k)) {
return true;
}
}
}
return false;
}
bool StackTransform::is_pu2xc(int i, int j, int k) const {
StackTransform t;
return is_valid() && d == -2 && dp >= 1 // basic checks
&& t.apply_push(i) && t.apply_xchg(0, 1) // PUSH s(i) ; XCHG s0,s1
&& t.apply_push(j + 1) && t.apply_xchg(0, 1) // PUSH s(j+1) ; XCHG s0,s1
&& t.apply_xchg(0, k + 2) && t <= *this; // XCHG s0,s(k+2)
}
// PUSH s(i) ; PUSH s(j+1) ; PUSH s(k+2)
bool StackTransform::is_push3(int i, int j, int k) const {
StackTransform t;
return is_valid() && d == -3 && t.apply_push(i) && t.apply_push(j + 1) && t.apply_push(k + 2) && t <= *this;
}
bool StackTransform::is_push3(int *i, int *j, int *k) const {
if (!is_valid() || d != -3 || n > 3) {
return false;
}
*i = get(2);
*j = get(1);
*k = get(0);
return is_push3(*i, *j, *k);
}
bool StackTransform::is_blkswap(int *i, int *j) const {
if (!is_valid() || d || !is_permutation()) {
return false;
}
*j = get(0);
if (*j <= 0) {
return false;
}
auto v = preimage(0);
if (v.size() != 1) {
return false;
}
*i = v[0];
return *i > 0 && is_blkswap(*i, *j);
}
bool StackTransform::is_blkswap(int i, int j) const {
if (!is_valid() || d || i <= 0 || j <= 0 || dp < i + j || !is_trivial_after(i + j)) {
return false;
}
for (int s = 0; s < i; s++) {
if (get(s) != s + j) {
return false;
}
}
for (int s = 0; s < j; s++) {
if (get(s + i) != s) {
return false;
}
}
return true;
}
// equivalent to i times DROP
bool StackTransform::is_blkdrop(int *i) const {
if (is_valid() && d > 0 && !n) {
*i = d;
return true;
}
return false;
}
// equivalent to i times PUSH s(j)
bool StackTransform::is_blkpush(int *i, int *j) const {
if (!is_valid() || d >= 0) {
return false;
}
*i = -d;
*j = get(*i - 1);
return is_blkpush(*i, *j);
}
bool StackTransform::is_blkpush(int i, int j) const {
if (!is_valid() || d >= 0 || d != -i || j < 0 || dp < i + j || !is_trivial_after(i)) {
return false;
}
StackTransform t;
for (int s = 0; s < i; s++) {
if (!t.apply_push(j)) {
return false;
}
}
return t <= *this;
}
bool StackTransform::is_reverse(int *i, int *j) const {
if (!is_valid() || d || !is_permutation() || n < 2) {
return false;
}
*j = A[0].first;
*i = A[n - 1].first - A[0].first + 1;
return is_reverse(*i, *j);
}
bool StackTransform::is_reverse(int i, int j) const {
if (!is_valid() || d || !is_trivial_after(i + j) || n < 2 || A[0].first != j || A[n - 1].first != j + i - 1) {
return false;
}
for (int s = 0; s < i; s++) {
if (get(j + s) != j + i - 1 - s) {
return false;
}
}
return true;
}
// 0 i+1 i+2 ... == i*NIP
// j i+1 i+2 ... == XCHG s(i),s(j) ; BLKDROP i
bool StackTransform::is_nip_seq(int i, int j) const {
return is_valid() && d == i && i > j && j >= 0 && n == 1 && A[0].first == i && A[0].second == j;
}
bool StackTransform::is_nip_seq(int *i) const {
*i = d;
return is_nip_seq(*i);
}
bool StackTransform::is_nip_seq(int *i, int *j) const {
if (is_valid() && n > 0) {
*i = d;
*j = A[0].second;
return is_nip_seq(*i, *j);
} else {
return false;
}
}
void StackTransform::show(std::ostream &os, int mode) const {
if (!is_valid()) {
os << "<invalid>";
return;
}
int mi = 0, ma = 0;
if (n > 0 && A[0].first < d) {
mi = A[0].first - d;
}
if (n > 0) {
ma = std::max(ma, A[n - 1].first - d + 1);
}
ma = std::max(ma + 1, dp - d);
os << '{';
if (dp == d) {
os << '|';
}
for (int i = mi; i < ma; i++) {
os << get(i) << (i == -1 ? '?' : (i == dp - d - 1 ? '|' : ' '));
}
os << get(ma) << "..}";
}
} // namespace funC

85
crypto/func/test/a6.fc Normal file
View file

@ -0,0 +1,85 @@
(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);
}
-}
{-
< type A, type B, type C >
(B, C, A) rot (A x, B y, C z) {
return (y, z, x);
}
-}
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;
}

75
crypto/func/test/a6.fp Normal file
View file

@ -0,0 +1,75 @@
f(int a, int b, int c, int d, int e, int f) : (int, int) {
var D = a * d - b * c;
var Dx : int = e * d - b * f;
var Dy : int = a * f - e * c;
return (Dx / D, Dy / D);
}
calc_phi() : int = {
var n : int = 1;
repeat (10) {
n *= 10;
}
var p = var q = 1;
do {
(p, q) = (q, p + q);
} until q > n;
return muldivr(p, n, q);
}
calc_sqrt2() : int = {
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);
}
calc_phi() : int = {
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);
}
operator _/%_ infix 20;
(x : int) /% (y : int) : (int, int) = {
return (x / y, x % y);
}
{-
_/%_ (int x, int y) : (int, int) = {
return (x / y, x % y);
}
-}
rot < A : type, B : type, C : type >
(x : A, y : B, z : C) : (B, C, A) {
return (y, z, x);
}
ataninv(base : int, q : int) : int { ;; computes base*atan(1/q)
base /~= q;
q *= - q;
var sum : int = 0;
var n = 1;
do {
sum += base /~ n;
base /~= q;
n += 2;
} while base;
return sum;
}
calc_pi() : int {
var base = 64;
repeat (70) { base *= 10; }
return (ataninv(base << 2, 5) - ataninv(base, 239)) >>~ 4;
}

6
crypto/func/test/a6_1.fc Normal file
View file

@ -0,0 +1,6 @@
(int, int) f(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);
}

130
crypto/func/test/a6_2.fc Normal file
View file

@ -0,0 +1,130 @@
var f(a, b, c, d, e, f) {
var D = a * d - b * c;
var Dx = e * d - b * f;
var Dy = a * f - e * c;
return (Dx / D, Dy / D);
}
var test8() {
return -4;
}
var test3(a) {
int x = a * 10;
a += 1;
int x = x + 3;
{
int y = x + 2;
x = y + a;
}
int z = 5;
return x * (z + a);
}
var test2(a) {
(var x, var y) = a /% 10;
return x + y;
}
var test2a(a) {
var (x, y) = a /% 10;
return x + y;
}
var test2b(a) {
var z = a /% 10;
return _+_ z;
}
var test2c(a) {
return _+_ (a /% 10);
}
var twice(f, x) {
return f (f x);
}
var test() {
return f(1, 2, 3, 4, 7, 17);
}
var rot(a, b, c) {
return (b, c, a);
}
var rot_int(int a, int b, int c) {
return (b, c, a);
}
(int, _) dup_int(x) {
return (x, x);
}
var calc_phi() {
var n = 10;
n *= n;
n *= n;
n *= n;
n *= n;
n *= n;
n *= n;
var p = var q = 1;
(p, q) = (q, p + q);
(p, q) = (q, p + q);
(p, q) = (q, p + q);
(p, q) = (q, p + q);
(p, q) = (q, p + q);
(p, q) = (q, p + q);
(p, q) = (q, p + q);
return muldivr(p, n, q);
}
var t0() {
var x = 1;
return x;
}
var t1() {
return 2;
}
var t2(int x) {
return 2;
return x += 1;
}
var t3(int x, var y) {
int z = (x + y) * (x - y);
}
var t3b(int x, var y) {
int z = (x + y) * (x - y);
return z;
}
var t4((int, int, int) z) {
var (_, u, _) = z;
return u;
}
int foo(int t);
var t5(x) {
var f = t2;
return twice(f, x) * f(x);
}
var proj1(a, b) { return a; }
var proj1a(a, b) { var c = a; return c; }
var proj1b(a, b) { int c = a; return c; }
var proj1c(a, b) { var c = a + 2; return c; }
var proj1d(a, b) { return a + 2; }
var proj1e(int a, _) { return a; }
int proj1f(_ a, int) { return a; }
int proj1g(int, a) { return a; }
var test1(a) {
a = a + 1;
return a;
}

12
crypto/func/test/a7.fc Normal file
View file

@ -0,0 +1,12 @@
int steps(int x) {
var n = 0;
while (x > 1) {
n += 1;
if (x & 1) {
x = 3 * x + 1;
} else {
x >>= 1;
}
}
return n;
}

28
crypto/func/test/a8.fc Normal file
View file

@ -0,0 +1,28 @@
int A(int a, int b, int c, int d, int e, int f) {
return muldiv(a, b, c) + muldiv(d, e, f);
}
int B(int a, int b, int c, int d, int e, int f) {
return muldiv(a, b, c) + muldiv(b, d, e) + f;
}
int C(int a, int b, int c, int d, int e, int f) {
return muldiv(a, b, c) + muldiv(b, d, c) + muldiv(d, d, f);
}
int D(int a, int b, int c, int d, int e, int f) {
return muldiv(a, b, c) + muldiv(b, d, c) + muldiv(e, e, f);
}
int E(int a, int b, int c, int d, int e, int f) {
return muldiv(a, b, c) + muldiv(c, d, e) + muldiv(c, c, f);
}
int F(int a, int b, int c, int d, int e, int f) {
return muldiv(a, b, c) + muldiv(a, d, c) + muldiv(f, f, e);
}
int G(int a, int b, int c, int d, int e, int f) {
return muldiv(a, b, c) + muldiv(b, c, d) + muldiv(b, c, e) + f;
}

23
crypto/func/test/a9.fc Normal file
View file

@ -0,0 +1,23 @@
int f(int x) {
if (2 * x + 1 == 7) {
return x + 17;
} else {
return 239;
}
}
int f2(int x) {
return 2 * x + 1 == 6 ? x + 17 : 239;
}
int g(int x) {
return x & 1 ? 3 * x + 1 : x / 2;
}
_ H(int x, int y) {
return (x < y, x <= y, x == y, x >= y, x > y, x != y, x <=> y);
}
int q() {
return 4;
}

28
crypto/func/test/a9_1.fc Normal file
View file

@ -0,0 +1,28 @@
_ F(int x) {
x = 2;
return x + 1;
}
_ G(x) {
var y = x + 1;
x = 2;
return (x - 1, y);
}
_ H(x) {
var y = x + 1;
x = 2;
return (x * y, y);
}
_ I(x) {
int y = 17;
int z = x - y;
return (z, y);
}
_ J(x) {
int y = 239;
int z = x - y;
return (z, y);
}

63
crypto/func/test/b1.fc Normal file
View file

@ -0,0 +1,63 @@
int now() asm "NOW";
int cell_hash(cell c)
asm "HASHCU";
int slice_hash(slice s)
asm "HASHSU";
int check_signature(int hash, slice signature, int public_key)
asm "CHKSIGNU";
;; () throw_if(int excno, int cond) impure
;; asm "THROWARGIF";
cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP";
() accept_message() impure asm "ACCEPT";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";
(cell, slice) load_ref(slice s) asm "LDREF";
(int, slice) zload_int(slice s, int len) asm "LDIX";
(int, slice) zload_uint(slice s, int len) asm "LDUX";
int zpreload_int(slice s, int len) asm "PLDIX";
int zpreload_uint(slice s, int len) asm "PLDUX";
(slice, slice) load_bits(slice s, int len) asm "LDSLICEX";
slice preload_bits(slice s, int len) asm "PLDSLICEX";
cell set_idict_ref(cell value, int index, cell dict, int key_len) asm "DICTISETREF";
builder begin_cell() asm "NEWC";
builder store_ref(cell c, builder b) asm "STREF";
builder zstore_uint(int x, builder b, int len) asm "STUX";
builder zstore_int(int x, builder b, int len) asm "STIX";
cell end_cell(builder b) asm "ENDC";
;; Simple configuration smart contract
() recv_internal(cell in_msg) impure {
;; do nothing for internal messages
}
() recv_external(cell in_msg) impure {
var (signature, cs0) = load_bits(begin_parse(in_msg), 512);
var (msg_seqno, cs) = zload_uint(cs0, 32);
(var valid_until, cs) = zload_uint(cs, 32);
throw_if(35, valid_until < now());
var (cfg_dict, cs2) = load_ref(begin_parse(get_data()));
(var stored_seqno, cs2) = zload_uint(cs2, 32);
(var public_key, cs2) = zload_uint(cs2, 256);
end_parse(cs2);
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, check_signature(slice_hash(cs0), signature, public_key));
accept_message();
(var param_index, cs) = zload_uint(cs, 32);
(var param_value, cs) = load_ref(cs);
end_parse(cs);
;; cfg_dict = set_idict_ref(param_value, param_index, cfg_dict, 32);
;; var cb = begin_cell();
;; cb = store_ref(cfg_dict, cb);
var cb = store_ref(set_idict_ref(param_value, param_index, cfg_dict, 32), begin_cell());
cb = zstore_uint(stored_seqno + 1, cb, 32);
cb = zstore_uint(public_key, cb, 256);
set_data(end_cell(cb));
}

60
crypto/func/test/b2.fc Normal file
View file

@ -0,0 +1,60 @@
int now() asm "NOW";
int cell_hash(cell c)
asm "HASHCU";
int slice_hash(slice s)
asm "HASHSU";
int check_signature(int hash, slice signature, int public_key)
asm "CHKSIGNU";
;; () throw_if(int excno, int cond) impure
;; asm "THROWARGIF";
cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP";
() accept_message() impure asm "ACCEPT";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
;; (slice, int) load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
;; (slice, int) load_uint(slice s, int len) asm( -> 1 0) "LDUX";
;; int preload_int(slice s, int len) asm "PLDIX";
;; int preload_uint(slice s, int len) asm "PLDUX";
(slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";
slice preload_bits(slice s, int len) asm "PLDSLICEX";
cell set_idict_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
builder begin_cell() asm "NEWC";
builder store_ref(builder b, cell c) asm(c b) "STREF";
;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
;; builder store_int(builder b, int x, int len) asm(x b len) "STIX";
cell end_cell(builder b) asm "ENDC";
;; Simple configuration smart contract
() recv_internal(cell in_msg) impure {
;; do nothing for internal messages
}
() recv_external(cell in_msg) impure {
var (cs0, signature) = load_bits(begin_parse(in_msg), 512);
var (cs, msg_seqno) = load_uint(cs0, 32);
(cs, var valid_until) = load_uint(cs, 32);
throw_if(35, valid_until < now());
var (cs2, cfg_dict) = load_ref(begin_parse(get_data()));
(cs2, var stored_seqno) = load_uint(cs2, 32);
(cs2, var public_key) = load_uint(cs2, 256);
end_parse(cs2);
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, check_signature(slice_hash(cs0), signature, public_key));
accept_message();
(cs, var param_index) = load_uint(cs, 32);
(cs, var param_value) = load_ref(cs);
end_parse(cs);
var cb = store_ref(begin_cell(), set_idict_ref(cfg_dict, 32, param_index, param_value));
cb = store_uint(cb, stored_seqno + 1, 32);
cb = store_uint(cb, public_key, 256);
set_data(end_cell(cb));
}

63
crypto/func/test/b2_0.fc Normal file
View file

@ -0,0 +1,63 @@
int now() asm "NOW";
int cell_hash(cell c)
asm "HASHCU";
int slice_hash(slice s)
asm "HASHSU";
int check_signature(int hash, slice signature, int public_key)
asm "CHKSIGNU";
;; () throw_if(int excno, int cond) impure
;; asm "THROWARGIF";
cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP";
() accept_message() impure asm "ACCEPT";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
(slice, int) zload_int(slice s, int len) asm(s len -> 1 0) "LDIX";
(slice, int) zload_uint(slice s, int len) asm( -> 1 0) "LDUX";
int zpreload_int(slice s, int len) asm "PLDIX";
int zpreload_uint(slice s, int len) asm "PLDUX";
(slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";
slice preload_bits(slice s, int len) asm "PLDSLICEX";
cell set_idict_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
builder begin_cell() asm "NEWC";
builder store_ref(builder b, cell c) asm(c b) "STREF";
builder zstore_uint(builder b, int x, int len) asm(x b len) "STUX";
builder zstore_int(builder b, int x, int len) asm(x b len) "STIX";
cell end_cell(builder b) asm "ENDC";
;; Simple configuration smart contract
() recv_internal(cell in_msg) impure {
;; do nothing for internal messages
}
() recv_external(cell in_msg) impure {
var (cs0, signature) = load_bits(begin_parse(in_msg), 512);
var (cs, msg_seqno) = zload_uint(cs0, 32);
(cs, var valid_until) = zload_uint(cs, 32);
throw_if(35, valid_until < now());
var (cs2, cfg_dict) = load_ref(begin_parse(get_data()));
(cs2, var stored_seqno) = zload_uint(cs2, 32);
(cs2, var public_key) = zload_uint(cs2, 256);
end_parse(cs2);
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, check_signature(slice_hash(cs0), signature, public_key));
accept_message();
(cs, var param_index) = zload_uint(cs, 32);
(cs, var param_value) = load_ref(cs);
end_parse(cs);
;; cfg_dict = set_idict_ref(cfg_dict, 32, param_index, param_value);
;; var cb = begin_cell();
;; cb = store_ref(cb, cfg_dict);
var cb = store_ref(begin_cell(), set_idict_ref(cfg_dict, 32, param_index, param_value));
cb = zstore_uint(cb, stored_seqno + 1, 32);
cb = zstore_uint(cb, public_key, 256);
set_data(end_cell(cb));
}

62
crypto/func/test/b2_1.fc Normal file
View file

@ -0,0 +1,62 @@
int now() asm "NOW";
int cell_hash(cell c)
asm "HASHCU";
int slice_hash(slice s)
asm "HASHSU";
int check_signature(int hash, slice signature, int public_key)
asm "CHKSIGNU";
;; () throw_if(int excno, int cond) impure
;; asm "THROWARGIF";
cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP";
() accept_message() impure asm "ACCEPT";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX";
;; int preload_int(slice s, int len) asm "PLDIX";
;; int preload_uint(slice s, int len) asm "PLDUX";
(slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";
slice preload_bits(slice s, int len) asm "PLDSLICEX";
cell set_idict_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
(cell, ()) ~set_idict_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
builder begin_cell() asm "NEWC";
builder store_ref(builder b, cell c) asm(c b) "STREF";
;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
;; builder store_int(builder b, int x, int len) asm(x b len) "STIX";
cell end_cell(builder b) asm "ENDC";
;; Simple configuration smart contract
() recv_internal(cell in_msg) impure {
;; do nothing for internal messages
}
() recv_external(cell in_msg) impure {
var cs = begin_parse(in_msg);
var signature = cs~load_bits(512);
var cs0 = cs;
int msg_seqno = cs~load_uint(32);
var valid_until = cs~load_uint(32);
throw_if(35, valid_until < now());
var cs2 = begin_parse(get_data());
var cfg_dict = cs2~load_ref();
var stored_seqno = cs2~load_uint(32);
var public_key = cs2~load_uint(256);
cs2.end_parse();
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, check_signature(slice_hash(cs0), signature, public_key));
accept_message();
var param_index = cs~load_uint(32);
var param_value = cs~load_ref();
cs.end_parse();
cfg_dict~set_idict_ref(32, param_index, param_value);
set_data(begin_cell().store_ref(cfg_dict).store_uint(stored_seqno + 1, 32).store_uint(public_key, 256).end_cell());
}

58
crypto/func/test/b2_2.fc Normal file
View file

@ -0,0 +1,58 @@
int now() asm "NOW";
int cell_hash(cell c)
asm "HASHCU";
int slice_hash(slice s)
asm "HASHSU";
int check_signature(int hash, slice signature, int public_key)
asm "CHKSIGNU";
;; () throw_if(int excno, int cond) impure
;; asm "THROWARGIF";
cell get_data() asm "c4 PUSH";
() set_data(cell c) impure asm "c4 POP";
() accept_message() impure asm "ACCEPT";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
;; (slice, int) load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
;; (slice, int) load_uint(slice s, int len) asm( -> 1 0) "LDUX";
;; int .preload_int(slice s, int len) asm "PLDIX";
;; int .preload_uint(slice s, int len) asm "PLDUX";
(slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";
;; slice .preload_bits(slice s, int len) asm "PLDSLICEX";
cell set_idict_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
builder begin_cell() asm "NEWC";
builder store_ref(builder b, cell c) asm(c b) "STREF";
;;builder .store_uint(builder b, int x, int len) asm(x b len) "STUX";
;;builder .store_int(builder b, int x, int len) asm(x b len) "STIX";
cell .end_cell(builder b) asm "ENDC";
;; Simple configuration smart contract
() recv_internal(cell in_msg) impure {
;; do nothing for internal messages
}
() recv_external(cell in_msg) impure {
var (cs0, signature) = load_bits(begin_parse(in_msg), 512);
var (cs, msg_seqno) = load_uint(cs0, 32);
(cs, var valid_until) = load_uint(cs, 32);
throw_if(35, valid_until < now());
var (cs2, cfg_dict) = load_ref(begin_parse(get_data()));
(cs2, var stored_seqno) = load_uint(cs2, 32);
(cs2, var public_key) = load_uint(cs2, 256);
end_parse(cs2);
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, check_signature(slice_hash(cs0), signature, public_key));
accept_message();
(cs, var param_index) = load_uint(cs, 32);
(cs, var param_value) = load_ref(cs);
end_parse(cs);
set_data(begin_cell().store_ref(cfg_dict.set_idict_ref(32, param_index, param_value))
.store_uint(stored_seqno + 1, 32).store_uint(public_key, 256).end_cell());
}

384
crypto/func/unify-types.cpp Normal file
View file

@ -0,0 +1,384 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "func.h"
namespace funC {
/*
*
* TYPE EXPRESSIONS
*
*/
int TypeExpr::holes = 0, TypeExpr::type_vars = 0; // not thread safe, but it is ok for now
void TypeExpr::compute_width() {
switch (constr) {
case te_Atomic:
case te_Map:
minw = maxw = 1;
break;
case te_Tensor:
minw = maxw = 0;
for (TypeExpr* arg : args) {
minw += arg->minw;
maxw += arg->maxw;
}
if (minw > w_inf) {
minw = w_inf;
}
if (maxw > w_inf) {
maxw = w_inf;
}
break;
case te_Indirect:
minw = args[0]->minw;
maxw = args[0]->maxw;
break;
default:
minw = 0;
maxw = w_inf;
break;
}
}
bool TypeExpr::recompute_width() {
switch (constr) {
case te_Tensor:
case te_Indirect: {
int min = 0, max = 0;
for (TypeExpr* arg : args) {
min += arg->minw;
max += arg->maxw;
}
if (min > maxw || max < minw) {
return false;
}
if (min > w_inf) {
min = w_inf;
}
if (max > w_inf) {
max = w_inf;
}
if (minw < min) {
minw = min;
}
if (maxw > max) {
maxw = max;
}
return true;
}
default:
return false;
}
}
int TypeExpr::extract_components(std::vector<TypeExpr*>& comp_list) {
if (constr != te_Indirect && constr != te_Tensor) {
comp_list.push_back(this);
return 1;
}
int res = 0;
for (TypeExpr* arg : args) {
res += arg->extract_components(comp_list);
}
return res;
}
TypeExpr* TypeExpr::new_map(TypeExpr* from, TypeExpr* to) {
return new TypeExpr{te_Map, std::vector<TypeExpr*>{from, to}};
}
void TypeExpr::replace_with(TypeExpr* te2) {
if (te2 == this) {
return;
}
constr = te_Indirect;
value = 0;
minw = te2->minw;
maxw = te2->maxw;
args.clear();
args.push_back(te2);
}
bool TypeExpr::remove_indirect(TypeExpr*& te, TypeExpr* forbidden) {
assert(te);
while (te->constr == te_Indirect) {
te = te->args[0];
}
if (te->constr == te_Unknown) {
return te != forbidden;
}
bool res = true;
for (auto& x : te->args) {
res &= remove_indirect(x, forbidden);
}
return res;
}
bool TypeExpr::remove_forall(TypeExpr*& te) {
assert(te);
if (te->constr != te_ForAll) {
return false;
}
assert(te->args.size() >= 1);
std::vector<TypeExpr*> new_vars;
for (std::size_t i = 1; i < te->args.size(); i++) {
new_vars.push_back(new_hole(1));
}
TypeExpr* te2 = te;
// std::cerr << "removing universal quantifier in " << te << std::endl;
te = te->args[0];
remove_forall_in(te, te2, new_vars);
// std::cerr << "-> " << te << std::endl;
return true;
}
bool TypeExpr::remove_forall_in(TypeExpr*& te, TypeExpr* te2, const std::vector<TypeExpr*>& new_vars) {
assert(te);
assert(te2 && te2->constr == te_ForAll);
if (te->constr == te_Unknown) {
for (std::size_t i = 0; i < new_vars.size(); i++) {
if (te == te2->args[i + 1]) {
te = new_vars[i];
return true;
}
}
return false;
}
if (te->constr == te_ForAll) {
return false;
}
if (te->args.empty()) {
return false;
}
auto te1 = new TypeExpr(*te);
bool res = false;
for (auto& arg : te1->args) {
res |= remove_forall_in(arg, te2, new_vars);
}
if (res) {
te = te1;
} else {
delete te1;
}
return res;
}
void TypeExpr::show_width(std::ostream& os) {
os << minw;
if (maxw != minw) {
os << "..";
if (maxw < w_inf) {
os << maxw;
}
}
}
std::ostream& operator<<(std::ostream& os, TypeExpr* type_expr) {
if (!type_expr) {
return os << "(null-type-ptr)";
}
return type_expr->print(os);
}
std::ostream& TypeExpr::print(std::ostream& os, int lex_level) {
switch (constr) {
case te_Unknown:
if (value >= 0) {
return os << "??" << value;
} else if (value >= -26) {
return os << (char)(64 - value);
} else {
return os << "TVAR" << -value;
}
case te_Indirect:
return os << args[0];
case te_Atomic: {
switch (value) {
case _Int:
return os << "int";
case _Cell:
return os << "cell";
case _Slice:
return os << "slice";
case _Builder:
return os << "builder";
case _Cont:
return os << "cont";
case _Tuple:
return os << "tuple";
case _Type:
return os << "type";
default:
return os << "atomic-type-" << value;
}
}
case te_Tensor: {
os << "(";
auto c = args.size();
if (c) {
for (const auto& x : args) {
x->print(os);
if (--c) {
os << ", ";
}
}
}
return os << ")";
}
case te_Map: {
assert(args.size() == 2);
if (lex_level > 0) {
os << "(";
}
args[0]->print(os, 1);
os << " -> ";
args[1]->print(os);
if (lex_level > 0) {
os << ")";
}
return os;
}
case te_ForAll: {
assert(args.size() >= 1);
if (lex_level > 0) {
os << '(';
}
os << "Forall ";
for (std::size_t i = 1; i < args.size(); i++) {
os << (i > 1 ? ' ' : '(');
args[i]->print(os);
}
os << ") ";
args[0]->print(os);
if (lex_level > 0) {
os << ')';
}
return os;
}
default:
return os << "unknown-type-expr-" << constr;
}
}
void UnifyError::print_message(std::ostream& os) const {
os << "cannot unify type " << te1 << " with " << te2;
if (!msg.empty()) {
os << ": " << msg;
}
}
std::ostream& operator<<(std::ostream& os, const UnifyError& ue) {
ue.print_message(os);
return os;
}
std::string UnifyError::message() const {
std::ostringstream os;
UnifyError::print_message(os);
return os.str();
}
void check_width_compat(TypeExpr* te1, TypeExpr* te2) {
if (te1->minw > te2->maxw || te2->minw > te1->maxw) {
std::ostringstream os{"cannot unify types of widths "};
te1->show_width(os);
os << " and ";
te2->show_width(os);
throw UnifyError{te1, te2, os.str()};
}
}
void check_update_widths(TypeExpr* te1, TypeExpr* te2) {
check_width_compat(te1, te2);
te1->minw = te2->minw = std::max(te1->minw, te2->minw);
te1->maxw = te2->maxw = std::min(te1->maxw, te2->maxw);
assert(te1->minw <= te2->minw);
}
void unify(TypeExpr*& te1, TypeExpr*& te2) {
assert(te1 && te2);
// std::cerr << "unify( " << te1 << " , " << te2 << " )\n";
while (te1->constr == TypeExpr::te_Indirect) {
te1 = te1->args[0];
}
while (te2->constr == TypeExpr::te_Indirect) {
te2 = te2->args[0];
}
if (te1 == te2) {
return;
}
if (te1->constr == TypeExpr::te_ForAll) {
TypeExpr* te = te1;
if (!TypeExpr::remove_forall(te)) {
throw UnifyError{te1, te2, "cannot remove universal type quantifier while performing type unification"};
}
unify(te, te2);
return;
}
if (te2->constr == TypeExpr::te_ForAll) {
TypeExpr* te = te2;
if (!TypeExpr::remove_forall(te)) {
throw UnifyError{te2, te1, "cannot remove universal type quantifier while performing type unification"};
}
unify(te1, te);
return;
}
if (te1->constr == TypeExpr::te_Unknown) {
if (te2->constr == TypeExpr::te_Unknown) {
assert(te1->value != te2->value);
}
if (!TypeExpr::remove_indirect(te2, te1)) {
throw UnifyError{te1, te2, "type unification results in an infinite cyclic type"};
}
check_update_widths(te1, te2);
te1->replace_with(te2);
te1 = te2;
return;
}
if (te2->constr == TypeExpr::te_Unknown) {
if (!TypeExpr::remove_indirect(te1, te2)) {
throw UnifyError{te2, te1, "type unification results in an infinite cyclic type"};
}
check_update_widths(te2, te1);
te2->replace_with(te1);
te2 = te1;
return;
}
if (te1->constr != te2->constr || te1->value != te2->value || te1->args.size() != te2->args.size()) {
throw UnifyError{te1, te2};
}
for (std::size_t i = 0; i < te1->args.size(); i++) {
unify(te1->args[i], te2->args[i]);
}
if (te1->constr == TypeExpr::te_Tensor) {
if (!te1->recompute_width()) {
throw UnifyError{te1, te2, "type unification incompatible with known width of first type"};
}
if (!te2->recompute_width()) {
throw UnifyError{te2, te1, "type unification incompatible with known width of first type"};
}
check_update_widths(te1, te2);
}
te1->replace_with(te2);
te1 = te2;
}
} // namespace funC

256
crypto/openssl/bignum.cpp Normal file
View file

@ -0,0 +1,256 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "openssl/bignum.h"
// impl only
#include <cstring>
namespace arith {
BN_CTX* get_ctx(void) {
thread_local BN_CTX* ctx = BN_CTX_new();
return ctx;
}
BignumBitref& BignumBitref::operator=(bool val) {
if (val) {
BN_set_bit(ptr, n);
} else {
BN_clear_bit(ptr, n);
}
return *this;
}
const Bignum operator+(const Bignum& x, const Bignum& y) {
Bignum z;
bn_assert(BN_add(z.bn_ptr(), x.bn_ptr(), y.bn_ptr()));
return z;
}
const Bignum operator+(const Bignum& x, long y) {
if (y > 0) {
Bignum z(x);
bn_assert(BN_add_word(z.bn_ptr(), y));
return z;
} else if (y < 0) {
Bignum z(x);
bn_assert(BN_sub_word(z.bn_ptr(), -y));
return z;
} else {
return x;
}
}
/*
const Bignum operator+ (Bignum&& x, long y) {
if (y > 0) {
bn_assert (BN_add_word (x.bn_ptr(), y));
} else if (y < 0) {
bn_assert (BN_sub_word (x.bn_ptr(), -y));
}
return std::move (x);
}
*/
/*
const Bignum operator+ (long y, Bignum&& x) {
return x + y;
}
*/
const Bignum operator-(const Bignum& x, const Bignum& y) {
Bignum z;
bn_assert(BN_sub(z.bn_ptr(), x.bn_ptr(), y.bn_ptr()));
return z;
}
/*
const Bignum operator- (Bignum&& x, long y) {
return x + (-y);
}
*/
const Bignum operator*(const Bignum& x, const Bignum& y) {
Bignum z;
bn_assert(BN_mul(z.bn_ptr(), x.bn_ptr(), y.bn_ptr(), get_ctx()));
return z;
}
const Bignum operator*(const Bignum& x, long y) {
if (y > 0) {
Bignum z(x);
bn_assert(BN_mul_word(z.bn_ptr(), y));
return z;
} else if (y < 0) {
Bignum z(x);
z.negate();
bn_assert(BN_mul_word(z.bn_ptr(), -y));
return z;
} else {
Bignum z(0);
return z;
}
}
/*
const Bignum operator* (Bignum&& x, long y) {
if (y > 0) {
bn_assert (BN_mul_word (x.bn_ptr(), y));
} else if (y < 0) {
x.negate();
bn_assert (BN_mul_word (x.bn_ptr(), -y));
} else {
x = 0;
}
return std::move (x);
}
*/
const Bignum operator/(const Bignum& x, const Bignum& y) {
Bignum z, w;
bn_assert(BN_div(z.bn_ptr(), w.bn_ptr(), x.bn_ptr(), y.bn_ptr(), get_ctx()));
return z;
}
const Bignum Bignum::divmod(const Bignum& y) {
Bignum w;
bn_assert(BN_div(val, w.bn_ptr(), val, y.bn_ptr(), get_ctx()));
return w;
}
const Bignum operator%(const Bignum& x, const Bignum& y) {
Bignum z;
bn_assert(BN_mod(z.bn_ptr(), x.bn_ptr(), y.bn_ptr(), get_ctx()));
return z;
}
unsigned long operator%(const Bignum& x, unsigned long y) {
BN_ULONG rem = BN_mod_word(x.bn_ptr(), y);
bn_assert(rem != (BN_ULONG)(-1));
return static_cast<unsigned long>(rem);
}
const Bignum operator<<(const Bignum& x, int r) {
Bignum z;
bn_assert(BN_lshift(z.bn_ptr(), x.bn_ptr(), r));
return z;
}
const Bignum operator>>(const Bignum& x, int r) {
Bignum z;
bn_assert(BN_rshift(z.bn_ptr(), x.bn_ptr(), r));
return z;
}
const Bignum abs(const Bignum& x) {
Bignum T(x);
if (T.sign() < 0) {
T.negate();
}
return T;
}
const Bignum sqr(const Bignum& x) {
Bignum z;
bn_assert(BN_sqr(z.bn_ptr(), x.bn_ptr(), get_ctx()));
return z;
}
void Bignum::export_msb(unsigned char* buffer, std::size_t size) const {
bn_assert(size <= (1 << 20));
bn_assert(sign() >= 0);
std::size_t n = BN_num_bytes(val);
bn_assert(n <= size);
bn_assert(BN_bn2bin(val, buffer + size - n) == static_cast<int>(n));
std::memset(buffer, 0, size - n);
}
Bignum& Bignum::import_msb(const unsigned char* buffer, std::size_t size) {
bn_assert(size <= (1 << 20));
std::size_t i = 0;
while (i < size && !buffer[i]) {
i++;
}
bn_assert(BN_bin2bn(buffer + i, static_cast<int>(size - i), val) == val);
return *this;
}
void Bignum::export_lsb(unsigned char* buffer, std::size_t size) const {
bn_assert(size <= (1 << 20));
bn_assert(sign() >= 0);
std::size_t n = BN_num_bytes(val);
bn_assert(n <= size);
bn_assert(BN_bn2bin(val, buffer) == (int)n);
std::memset(buffer + n, 0, size - n);
for (std::size_t i = 0; 2 * i + 1 < n; i++) {
std::swap(buffer[i], buffer[n - 1 - i]);
}
}
Bignum& Bignum::import_lsb(const unsigned char* buffer, std::size_t size) {
bn_assert(size <= (1 << 20));
while (size > 0 && !buffer[size - 1]) {
size--;
}
if (!size) {
bn_assert(BN_zero(val));
return *this;
}
unsigned char tmp_buff[1024];
unsigned char* tmp = (size < 1024 ? tmp_buff : new unsigned char[size]);
unsigned char* ptr = tmp + size;
for (std::size_t i = 0; i < size; i++) {
*--ptr = buffer[i];
}
bn_assert(BN_bin2bn(tmp, static_cast<int>(size), val) == val);
if (tmp != tmp_buff) {
delete[] tmp;
}
return *this;
}
std::string Bignum::to_str() const {
char* ptr = BN_bn2dec(val);
std::string z(ptr);
OPENSSL_free(ptr);
return z;
}
std::string Bignum::to_hex() const {
char* ptr = BN_bn2hex(val);
std::string z(ptr);
OPENSSL_free(ptr);
return z;
}
std::ostream& operator<<(std::ostream& os, const Bignum& x) {
return os << x.to_str();
}
std::istream& operator>>(std::istream& is, Bignum& x) {
std::string word;
is >> word;
x = dec_string(word);
return is;
}
bool is_prime(const Bignum& p, int nchecks, bool trial_div) {
return BN_is_prime_fasttest_ex(p.bn_ptr(), BN_prime_checks, get_ctx(), trial_div, 0);
}
} // namespace arith

368
crypto/openssl/bignum.h Normal file
View file

@ -0,0 +1,368 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <string>
#include <iostream>
#include <openssl/bn.h>
#include "td/utils/bits.h"
namespace arith {
struct dec_string {
std::string str;
explicit dec_string(const std::string& s) : str(s) {
}
};
struct hex_string {
std::string str;
explicit hex_string(const std::string& s) : str(s) {
}
};
} // namespace arith
namespace arith {
inline void bn_assert(int cond);
BN_CTX* get_ctx();
class BignumBitref {
BIGNUM* ptr;
int n;
public:
BignumBitref(BIGNUM* x, int _n) : ptr(x), n(_n){};
operator bool() const {
return BN_is_bit_set(ptr, n);
}
BignumBitref& operator=(bool val);
};
class Bignum {
BIGNUM* val;
public:
class bignum_error {};
Bignum() {
val = BN_new();
}
Bignum(long x) {
val = BN_new();
set_long(x);
}
~Bignum() {
BN_free(val);
}
Bignum(const dec_string& ds) {
val = BN_new();
set_dec_str(ds.str);
}
Bignum(const hex_string& hs) {
val = BN_new();
set_hex_str(hs.str);
}
Bignum(const Bignum& x) {
val = BN_new();
BN_copy(val, x.val);
}
//Bignum (Bignum&& x) { val = x.val; }
void clear() {
BN_clear(val);
} // use this for sensitive data
Bignum& operator=(const Bignum& x) {
BN_copy(val, x.val);
return *this;
}
Bignum& operator=(Bignum&& x) {
swap(x);
return *this;
}
Bignum& operator=(long x) {
return set_long(x);
}
Bignum& operator=(const dec_string& ds) {
return set_dec_str(ds.str);
}
Bignum& operator=(const hex_string& hs) {
return set_hex_str(hs.str);
}
Bignum& swap(Bignum& x) {
BN_swap(val, x.val);
return *this;
}
BIGNUM* bn_ptr() {
return val;
}
const BIGNUM* bn_ptr() const {
return val;
}
bool is_zero() const {
return BN_is_zero(val);
}
int sign() const {
return BN_is_zero(val) ? 0 : (BN_is_negative(val) ? -1 : 1);
}
bool odd() const {
return BN_is_odd(val);
}
int num_bits() const {
return BN_num_bits(val);
}
int num_bytes() const {
return BN_num_bytes(val);
}
bool operator[](int n) const {
return BN_is_bit_set(val, n);
}
BignumBitref operator[](int n) {
return BignumBitref(val, n);
}
void export_msb(unsigned char* buffer, std::size_t size) const;
Bignum& import_msb(const unsigned char* buffer, std::size_t size);
Bignum& import_msb(const std::string& s) {
return import_msb((const unsigned char*)s.c_str(), s.size());
}
void export_lsb(unsigned char* buffer, std::size_t size) const;
Bignum& import_lsb(const unsigned char* buffer, std::size_t size);
Bignum& import_lsb(const std::string& s) {
return import_lsb((const unsigned char*)s.c_str(), s.size());
}
Bignum& set_dec_str(std::string s) {
bn_assert(BN_dec2bn(&val, s.c_str()));
return *this;
}
Bignum& set_hex_str(std::string s) {
bn_assert(BN_hex2bn(&val, s.c_str()));
return *this;
}
Bignum& set_ulong(unsigned long x) {
bn_assert(BN_set_word(val, x));
return *this;
}
Bignum& set_long(long x) {
set_ulong(std::abs(x));
return x < 0 ? negate() : *this;
}
Bignum& negate() {
BN_set_negative(val, !BN_is_negative(val));
return *this;
}
Bignum& operator+=(const Bignum& y) {
bn_assert(BN_add(val, val, y.val));
return *this;
}
Bignum& operator+=(long y) {
bn_assert((y >= 0 ? BN_add_word : BN_sub_word)(val, std::abs(y)));
return *this;
}
Bignum& operator-=(long y) {
bn_assert((y >= 0 ? BN_sub_word : BN_add_word)(val, std::abs(y)));
return *this;
}
Bignum& operator*=(const Bignum& y) {
bn_assert(BN_mul(val, val, y.val, get_ctx()));
return *this;
}
Bignum& operator*=(long y) {
if (y < 0) {
negate();
}
bn_assert(BN_mul_word(val, std::abs(y)));
return *this;
}
Bignum& operator<<=(int r) {
bn_assert(BN_lshift(val, val, r));
return *this;
}
Bignum& operator>>=(int r) {
bn_assert(BN_rshift(val, val, r));
return *this;
}
Bignum& operator/=(const Bignum& y) {
Bignum w;
bn_assert(BN_div(val, w.val, val, y.val, get_ctx()));
return *this;
}
Bignum& operator/=(long y) {
bn_assert(BN_div_word(val, std::abs(y)) != (BN_ULONG)(-1));
return y < 0 ? negate() : *this;
}
Bignum& operator%=(const Bignum& y) {
bn_assert(BN_mod(val, val, y.val, get_ctx()));
return *this;
}
Bignum& operator%=(long y) {
BN_ULONG rem = BN_mod_word(val, std::abs(y));
bn_assert(rem != (BN_ULONG)(-1));
return set_long(static_cast<long>(y < 0 ? td::bits_negate64(rem) : rem));
}
unsigned long divmod(unsigned long y) {
BN_ULONG rem = BN_div_word(val, y);
bn_assert(rem != (BN_ULONG)(-1));
return static_cast<unsigned long>(rem);
}
const Bignum divmod(const Bignum& y);
std::string to_str() const;
std::string to_hex() const;
};
inline void bn_assert(int cond) {
if (!cond) {
throw Bignum::bignum_error();
}
}
BN_CTX* get_ctx(void);
const Bignum operator+(const Bignum& x, const Bignum& y);
const Bignum operator+(const Bignum& x, long y);
/*
const Bignum operator+ (Bignum&& x, long y) {
if (y > 0) {
bn_assert (BN_add_word (x.bn_ptr(), y));
} else if (y < 0) {
bn_assert (BN_sub_word (x.bn_ptr(), -y));
}
return std::move (x);
}
*/
inline const Bignum operator+(long y, const Bignum& x) {
return x + y;
}
/*
const Bignum operator+ (long y, Bignum&& x) {
return x + y;
}
*/
const Bignum operator-(const Bignum& x, const Bignum& y);
inline const Bignum operator-(const Bignum& x, long y) {
return x + (-y);
}
/*
const Bignum operator- (Bignum&& x, long y) {
return x + (-y);
}
*/
const Bignum operator*(const Bignum& x, const Bignum& y);
const Bignum operator*(const Bignum& x, long y);
/*
const Bignum operator* (Bignum&& x, long y) {
if (y > 0) {
bn_assert (BN_mul_word (x.bn_ptr(), y));
} else if (y < 0) {
x.negate();
bn_assert (BN_mul_word (x.bn_ptr(), -y));
} else {
x = 0;
}
return std::move (x);
}
*/
inline const Bignum operator*(long y, const Bignum& x) {
return x * y;
}
const Bignum operator/(const Bignum& x, const Bignum& y);
const Bignum operator%(const Bignum& x, const Bignum& y);
unsigned long operator%(const Bignum& x, unsigned long y);
const Bignum operator<<(const Bignum& x, int r);
const Bignum operator>>(const Bignum& x, int r);
const Bignum abs(const Bignum& x);
const Bignum sqr(const Bignum& x);
std::ostream& operator<<(std::ostream& os, const Bignum& x);
std::istream& operator>>(std::istream& is, Bignum& x);
bool is_prime(const Bignum& p, int nchecks = 64, bool trial_div = true);
inline int cmp(const Bignum& x, const Bignum& y) {
return BN_cmp(x.bn_ptr(), y.bn_ptr());
}
inline bool operator==(const Bignum& x, const Bignum& y) {
return cmp(x, y) == 0;
}
inline bool operator!=(const Bignum& x, const Bignum& y) {
return cmp(x, y) != 0;
}
inline bool operator<(const Bignum& x, const Bignum& y) {
return cmp(x, y) < 0;
}
inline bool operator<=(const Bignum& x, const Bignum& y) {
return cmp(x, y) <= 0;
}
inline bool operator>(const Bignum& x, const Bignum& y) {
return cmp(x, y) > 0;
}
inline bool operator>=(const Bignum& x, const Bignum& y) {
return cmp(x, y) >= 0;
}
inline bool operator==(const Bignum& x, long y) {
if (y >= 0) {
return BN_is_word(x.bn_ptr(), y);
} else {
return x == Bignum(y);
}
}
inline bool operator!=(const Bignum& x, long y) {
if (y >= 0) {
return !BN_is_word(x.bn_ptr(), y);
} else {
return x != Bignum(y);
}
}
} // namespace arith

151
crypto/openssl/digest.h Normal file
View file

@ -0,0 +1,151 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <assert.h>
#include <openssl/evp.h>
#include <openssl/opensslv.h>
#include "td/utils/Slice.h"
namespace digest {
struct OpensslEVP_SHA1 {
enum { digest_bytes = 20 };
static const EVP_MD *get_evp() {
return EVP_sha1();
}
};
struct OpensslEVP_SHA256 {
enum { digest_bytes = 32 };
static const EVP_MD *get_evp() {
return EVP_sha256();
}
};
struct OpensslEVP_SHA512 {
enum { digest_bytes = 64 };
static const EVP_MD *get_evp() {
return EVP_sha512();
}
};
template <typename H>
class HashCtx {
EVP_MD_CTX *ctx{nullptr};
void init();
void clear();
public:
enum { digest_bytes = H::digest_bytes };
HashCtx() {
init();
}
HashCtx(const void *data, std::size_t len) {
init();
feed(data, len);
}
~HashCtx() {
clear();
}
void reset();
void feed(const void *data, std::size_t len);
void feed(td::Slice slice) {
feed(slice.data(), slice.size());
}
std::size_t extract(unsigned char buffer[digest_bytes]);
std::size_t extract(td::MutableSlice slice);
std::string extract();
};
template <typename H>
void HashCtx<H>::init() {
ctx = EVP_MD_CTX_create();
reset();
}
template <typename H>
void HashCtx<H>::reset() {
EVP_DigestInit_ex(ctx, H::get_evp(), 0);
}
template <typename H>
void HashCtx<H>::clear() {
EVP_MD_CTX_destroy(ctx);
ctx = nullptr;
}
template <typename H>
void HashCtx<H>::feed(const void *data, std::size_t len) {
EVP_DigestUpdate(ctx, data, len);
}
template <typename H>
std::size_t HashCtx<H>::extract(unsigned char buffer[digest_bytes]) {
unsigned olen = 0;
EVP_DigestFinal_ex(ctx, buffer, &olen);
assert(olen == digest_bytes);
return olen;
}
template <typename H>
std::size_t HashCtx<H>::extract(td::MutableSlice slice) {
return extract(slice.ubegin());
}
template <typename H>
std::string HashCtx<H>::extract() {
unsigned char buffer[digest_bytes];
unsigned olen = 0;
EVP_DigestFinal_ex(ctx, buffer, &olen);
assert(olen == digest_bytes);
return std::string((char *)buffer, olen);
}
typedef HashCtx<OpensslEVP_SHA1> SHA1;
typedef HashCtx<OpensslEVP_SHA256> SHA256;
typedef HashCtx<OpensslEVP_SHA512> SHA512;
template <typename T>
std::size_t hash_str(unsigned char buffer[T::digest_bytes], const void *data, std::size_t size) {
T hasher(data, size);
return hasher.extract(buffer);
}
template <typename T>
std::size_t hash_two_str(unsigned char buffer[T::digest_bytes], const void *data1, std::size_t size1, const void *data2,
std::size_t size2) {
T hasher(data1, size1);
hasher.feed(data2, size2);
return hasher.extract(buffer);
}
template <typename T>
std::string hash_str(const void *data, std::size_t size) {
T hasher(data, size);
return hasher.extract();
}
template <typename T>
std::string hash_two_str(const void *data1, std::size_t size1, const void *data2, std::size_t size2) {
T hasher(data1, size1);
hasher.feed(data2, size2);
return hasher.extract();
}
} // namespace digest

122
crypto/openssl/rand.cpp Normal file
View file

@ -0,0 +1,122 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "openssl/rand.hpp"
#include "td/utils/common.h"
#include <openssl/rand.h>
#include <openssl/opensslv.h>
namespace prng {
int os_get_random_bytes(void *buf, int n);
bool RandomGen::ok() const {
return RAND_status();
}
void RandomGen::seed_add(const void *data, std::size_t size, double entropy) {
RAND_add(data, static_cast<int>(size), entropy > 0 ? entropy : static_cast<double>(size));
}
void RandomGen::randomize(bool force) {
if (!force && ok()) {
return;
}
unsigned char buffer[128];
int n = os_get_random_bytes(buffer, 128);
seed_add(buffer, n);
assert(ok());
}
bool RandomGen::rand_bytes(void *data, std::size_t size, bool strong) {
#if OPENSSL_VERSION_NUMBER < 0x10101000L
int res = (strong ? RAND_bytes : RAND_pseudo_bytes)((unsigned char *)data, static_cast<int>(size));
#else
int res = RAND_bytes((unsigned char *)data, static_cast<int>(size));
#endif
if (res != 0 && res != 1) {
throw rand_error();
}
return res;
}
std::string RandomGen::rand_string(std::size_t size, bool strong) {
std::string result(size, '\0');
if (size > 0 && !rand_bytes(&result[0], size, strong)) {
throw rand_error();
}
return result;
}
RandomGen &rand_gen() {
// RandomGen is stateless, OpenSSL will handle concurrent access
static RandomGen MainPRNG;
return MainPRNG;
}
} // namespace prng
//------------------------- move to separate OS-dependent file?
#if TD_WINDOWS
namespace prng {
int os_get_random_bytes(void *buf, int n) {
return 0;
}
} // namespace prng
#else
#include <fcntl.h>
#include <unistd.h>
namespace prng {
int os_get_random_bytes(void *buf, int n) {
using namespace std;
int r = 0;
int h = open("/dev/random", O_RDONLY | O_NONBLOCK);
if (h >= 0) {
r = static_cast<int>(read(h, buf, n));
if (r > 0) {
//std::cerr << "added " << r << " bytes of real entropy to secure random numbers seed" << std::endl;
} else {
r = 0;
}
close(h);
}
if (r < n) {
h = open("/dev/urandom", O_RDONLY);
if (h < 0) {
return r;
}
int s = static_cast<int>(read(h, (char *)buf + r, n - r));
close(h);
if (s < 0) {
return r;
}
r += s;
}
if (r >= 8) {
*(long *)buf ^= lrand48();
srand48(*(long *)buf);
}
return r;
}
} // namespace prng
#endif

56
crypto/openssl/rand.hpp Normal file
View file

@ -0,0 +1,56 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <string>
#include <cassert>
namespace prng {
// use this generator unless need a separate one
class RandomGen;
RandomGen &rand_gen();
class RandomGen {
public:
struct rand_error {};
void randomize(bool force = true);
void seed_add(const void *data, std::size_t size, double entropy = 0);
bool ok() const;
RandomGen() {
randomize(false);
}
RandomGen(const void *seed, std::size_t size) {
seed_add(seed, size);
randomize(false);
}
bool rand_bytes(void *data, std::size_t size, bool strong = false);
bool strong_rand_bytes(void *data, std::size_t size) {
return rand_bytes(data, size, true);
}
template <class T>
bool rand_obj(T &obj) {
return rand_bytes(&obj, sizeof(T));
}
template <class T>
bool rand_objs(T *ptr, std::size_t count) {
return rand_bytes(ptr, sizeof(T) * count);
}
std::string rand_string(std::size_t size, bool strong = false);
};
} // namespace prng

176
crypto/openssl/residue.cpp Normal file
View file

@ -0,0 +1,176 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "residue.h"
// --- impl
#include <assert.h>
namespace arith {
class Residue;
class ResidueRing;
void ResidueRing::init() {
Zero = new Residue(0, td::Ref<ResidueRing>(this));
One = new Residue(1, td::Ref<ResidueRing>(this));
}
ResidueRing::~ResidueRing() {
delete Zero;
delete One;
delete Img_i;
Zero = One = Img_i = 0;
}
const Residue operator+(const Residue& x, const Residue& y) {
x.same_ring(y);
Residue z(x.ring_ref());
bn_assert(BN_mod_add(z.val.bn_ptr(), x.val.bn_ptr(), y.val.bn_ptr(), x.modulus().bn_ptr(), get_ctx()));
return z;
}
const Residue operator-(const Residue& x, const Residue& y) {
x.same_ring(y);
Residue z(x.ring_ref());
bn_assert(BN_mod_sub(z.val.bn_ptr(), x.val.bn_ptr(), y.val.bn_ptr(), x.modulus().bn_ptr(), get_ctx()));
return z;
}
const Residue operator*(const Residue& x, const Residue& y) {
x.same_ring(y);
Residue z(x.ring_ref());
bn_assert(BN_mod_mul(z.val.bn_ptr(), x.val.bn_ptr(), y.val.bn_ptr(), x.modulus().bn_ptr(), get_ctx()));
return z;
}
const Residue operator-(const Residue& x) {
Residue z(x);
z.val.negate();
return z.reduce();
}
Residue& Residue::operator+=(const Residue& y) {
same_ring(y);
bn_assert(BN_mod_add(val.bn_ptr(), val.bn_ptr(), y.val.bn_ptr(), modulus().bn_ptr(), get_ctx()));
return *this;
}
Residue& Residue::operator-=(const Residue& y) {
same_ring(y);
bn_assert(BN_mod_sub(val.bn_ptr(), val.bn_ptr(), y.val.bn_ptr(), modulus().bn_ptr(), get_ctx()));
return *this;
}
Residue& Residue::operator*=(const Residue& y) {
same_ring(y);
bn_assert(BN_mod_mul(val.bn_ptr(), val.bn_ptr(), y.val.bn_ptr(), modulus().bn_ptr(), get_ctx()));
return *this;
}
bool operator==(const Residue& x, const Residue& y) {
x.same_ring(y);
return x.extract() == y.extract();
}
bool operator!=(const Residue& x, const Residue& y) {
x.same_ring(y);
return x.extract() != y.extract();
}
Residue sqr(const Residue& x) {
Residue z(x.ring_ref());
bn_assert(BN_mod_sqr(z.val.bn_ptr(), x.val.bn_ptr(), x.modulus().bn_ptr(), get_ctx()));
return z;
}
Residue power(const Residue& x, const Bignum& y) {
Residue z(x.ring_ref());
bn_assert(BN_mod_exp(z.val.bn_ptr(), x.val.bn_ptr(), y.bn_ptr(), x.modulus().bn_ptr(), get_ctx()));
return z;
}
Residue inverse(const Residue& x) {
assert(x.ring_ref()->is_prime());
return power(x, x.ring_ref()->get_modulus() - 2);
}
const Residue& ResidueRing::img_i() const {
if (!Img_i) {
assert(is_prime());
assert(modulus % 4 == 1);
int g = 2;
Bignum n = (modulus - 1) / 4;
while (true) {
Residue t = power(frac(g), n);
if (t != one() && t != frac(-1)) {
Img_i = new Residue(t);
break;
}
g++;
}
}
return *Img_i;
}
Residue sqrt(const Residue& x) {
assert(x.ring_of().is_prime());
const ResidueRing& R = x.ring_of();
const Bignum& p = R.get_modulus();
if (x.is_zero() || !p.odd()) {
return x;
}
if (p[1]) { // p=3 (mod 4)
return power(x, (p + 1) >> 2);
} else if (p[2]) {
// p=5 (mod 8)
Residue t = power(x, (p + 3) >> 3);
return (sqr(t) == x) ? t : R.img_i() * t;
} else {
assert(p[2]);
return R.zero();
}
}
Residue ResidueRing::frac(long num, long denom) const {
assert(denom);
if (denom < 0) {
num = -num;
denom = -denom;
}
if (!(num % denom)) {
return Residue(num / denom, self_ref());
} else {
return Residue(num, self_ref()) * inverse(Residue(denom, self_ref()));
}
}
std::string Residue::to_str() const {
return "Mod(" + val.to_str() + "," + modulus().to_str() + ")";
}
std::ostream& operator<<(std::ostream& os, const Residue& x) {
return os << x.to_str();
}
std::istream& operator>>(std::istream& is, Residue& x) {
std::string word;
is >> word;
x = dec_string(word);
return is;
}
} // namespace arith

189
crypto/openssl/residue.h Normal file
View file

@ -0,0 +1,189 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "bignum.h"
#include "common/refcnt.hpp"
namespace arith {
class Residue;
class ResidueRing;
class ResidueRing : public td::CntObject {
public:
struct bad_modulus {};
private:
const Bignum modulus;
bool prime;
Residue* Zero;
Residue* One;
mutable Residue* Img_i;
void init();
public:
typedef Residue element;
explicit ResidueRing(Bignum mod) : modulus(mod), prime(arith::is_prime(mod)), Zero(0), One(0), Img_i(0) {
init();
}
~ResidueRing();
const Bignum& get_modulus() const {
return modulus;
}
bool is_prime() const {
return prime;
}
const Residue& zero() const {
return *Zero;
}
const Residue& one() const {
return *One;
}
const Residue& img_i() const;
Residue frac(long num, long denom = 1) const;
Residue convert(long num) const;
Residue convert(const Bignum& x) const;
Bignum reduce(const Bignum& x) const {
Bignum r = x % modulus;
if (r.sign() < 0) {
r += modulus;
}
return r;
}
Bignum& do_reduce(Bignum& x) const {
x %= modulus;
if (x.sign() < 0) {
x += modulus;
}
return x;
}
private:
td::Ref<ResidueRing> self_ref() const {
return td::Ref<ResidueRing>{this};
}
};
class Residue {
public:
struct not_same_ring {};
private:
td::Ref<ResidueRing> ring;
mutable Bignum val;
Residue& reduce() {
ring->do_reduce(val);
return *this;
}
public:
explicit Residue(td::Ref<ResidueRing> R) : ring(R) {
}
Residue(const Bignum& x, td::Ref<ResidueRing> R) : ring(R), val(R->reduce(x)) {
}
~Residue() {
}
Residue(const Residue& x) : ring(x.ring), val(x.val) {
}
Bignum extract() const {
return val;
}
const Bignum& extract_raw() const {
return val;
}
const Bignum& modulus() const {
return ring->get_modulus();
}
void same_ring(const Residue& y) const {
if (ring != y.ring) {
throw not_same_ring();
}
}
const ResidueRing& ring_of() const {
return *ring;
}
td::Ref<ResidueRing> ring_ref() const {
return ring;
}
bool is_zero() const {
return (val == 0);
}
Residue& operator=(const Residue& x) {
same_ring(x);
val = x.val;
return *this;
}
Residue& operator=(const Bignum& x) {
val = ring->reduce(x);
return *this;
}
Residue& operator+=(const Residue& y);
Residue& operator-=(const Residue& y);
Residue& operator*=(const Residue& y);
Residue& operator+=(long y) {
val += y;
return reduce();
}
Residue& operator-=(long y) {
val -= y;
return reduce();
}
Residue& operator*=(long y) {
val *= y;
return reduce();
}
Residue& negate() {
val.negate();
return reduce();
}
friend const Residue operator+(const Residue& x, const Residue& y);
friend const Residue operator-(const Residue& x, const Residue& y);
friend const Residue operator*(const Residue& x, const Residue& y);
friend const Residue operator-(const Residue& x);
friend Residue sqr(const Residue& x);
friend Residue power(const Residue& x, const Bignum& y);
friend Residue inverse(const Residue& x);
std::string to_str() const;
};
const Residue operator+(const Residue& x, const Residue& y);
const Residue operator-(const Residue& x, const Residue& y);
const Residue operator*(const Residue& x, const Residue& y);
const Residue operator-(const Residue& x);
bool operator==(const Residue& x, const Residue& y);
bool operator!=(const Residue& x, const Residue& y);
Residue sqr(const Residue& x);
Residue power(const Residue& x, const Bignum& y);
Residue inverse(const Residue& x);
Residue sqrt(const Residue& x);
inline Residue ResidueRing::convert(long x) const {
return Residue(x, td::Ref<ResidueRing>(this));
}
inline Residue ResidueRing::convert(const Bignum& x) const {
return Residue(x, td::Ref<ResidueRing>(this));
}
std::ostream& operator<<(std::ostream& os, const Residue& x);
std::istream& operator>>(std::istream& is, Residue& x);
} // namespace arith

288
crypto/parser/lexer.cpp Normal file
View file

@ -0,0 +1,288 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "lexer.h"
#include "symtable.h"
#include <sstream>
#include <cassert>
namespace src {
/*
*
* LEXER
*
*/
std::string Lexem::lexem_name_str(int idx) {
if (idx == Eof) {
return "end of file";
} else if (idx == Ident) {
return "identifier";
} else if (idx == Number) {
return "number";
} else if (idx == String) {
return "string";
} else if (idx == Special) {
return "special";
} else if (sym::symbols.get_keyword(idx)) {
return "`" + sym::symbols.get_keyword(idx)->str + "`";
} else {
std::ostringstream os{"<unknown lexem of type "};
os << idx << ">";
return os.str();
}
}
std::string Lexem::name_str() const {
if (tp == Ident) {
return std::string{"identifier `"} + sym::symbols.get_name(val) + "`";
} else if (tp == String) {
return std::string{"string \""} + str + '"';
} else {
return lexem_name_str(tp);
}
}
bool is_number(std::string str) {
auto st = str.begin(), en = str.end();
if (st == en) {
return false;
}
if (*st == '-') {
st++;
}
bool hex = false;
if (st + 1 < en && *st == '0' && st[1] == 'x') {
st += 2;
hex = true;
}
if (st == en) {
return false;
}
while (st < en) {
int c = *st;
if (c >= '0' && c <= '9') {
++st;
continue;
}
if (!hex) {
return false;
}
c |= 0x20;
if (c < 'a' || c > 'f') {
return false;
}
++st;
}
return true;
}
int Lexem::classify() {
if (tp != Unknown) {
return tp;
}
sym::sym_idx_t i = sym::symbols.lookup(str);
if (i) {
assert(str == sym::symbols[i]->str);
str = sym::symbols[i]->str;
sym::sym_idx_t idx = sym::symbols[i]->idx;
tp = (idx < 0 ? -idx : Ident);
val = i;
} else if (is_number(str)) {
tp = Number;
} else {
tp = lexem_is_special(str);
}
if (tp == Unknown) {
tp = Ident;
val = sym::symbols.lookup(str, 1);
}
return tp;
}
int Lexem::set(std::string _str, const SrcLocation& _loc, int _tp, int _val) {
str = _str;
loc = _loc;
tp = _tp;
val = _val;
return classify();
}
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::memset(char_class, 0, sizeof(char_class));
unsigned char activity = cc::active;
for (char c : active_chars) {
if (c == ' ') {
if (!--activity) {
activity = cc::allow_repeat;
}
} else if ((unsigned)c < 0x80) {
char_class[(unsigned)c] |= activity;
}
}
set_spec(eol_cmt, eol_cmts);
set_spec(cmt_op, open_cmts);
set_spec(cmt_cl, close_cmts);
for (int c : quote_chars) {
if (c > ' ' && c <= 0x7f) {
char_class[(unsigned)c] |= cc::quote_char;
}
}
if (init) {
next();
}
}
void Lexer::set_spec(std::array<int, 3>& arr, std::string setup) {
arr[0] = arr[1] = arr[2] = -0x100;
std::size_t n = setup.size(), i;
for (i = 0; i < n; i++) {
if (setup[i] == ' ') {
continue;
}
if (i == n - 1 || setup[i + 1] == ' ') {
arr[0] = setup[i];
} else if (i == n - 2 || (i < n - 2 && setup[i + 2] == ' ')) {
arr[1] = setup[i];
arr[2] = setup[++i];
} else {
while (i < n && setup[i] != ' ') {
i++;
}
}
}
}
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 " +
cur().name_str()};
}
next();
}
const Lexem& Lexer::next() {
if (peek_lexem.valid()) {
lexem = std::move(peek_lexem);
peek_lexem.clear({}, Lexem::Undefined);
eof = (lexem.tp == Lexem::Eof);
return lexem;
}
if (eof) {
return lexem.clear(src.here(), Lexem::Eof);
}
long long comm = 1;
while (!src.seek_eof()) {
int cc = src.cur_char(), nc = src.next_char();
if (cc == eol_cmt[0] || (cc == eol_cmt[1] && nc == eol_cmt[2])) {
src.load_line();
} else if (cc == cmt_op[1] && nc == cmt_op[2]) {
src.advance(2);
comm = comm * 2 + 1;
} else if (cc == cmt_op[0]) {
src.advance(1);
comm *= 2;
} else if (comm == 1) {
break;
} else if (cc == cmt_cl[1] && nc == cmt_cl[2]) {
if (!(comm & 1)) {
src.error(std::string{"a `"} + (char)cmt_op[0] + "` comment closed by `" + (char)cmt_cl[1] + (char)cmt_cl[2] +
"`");
}
comm >>= 1;
src.advance(2);
} else if (cc == cmt_cl[0]) {
if (!(comm & 1)) {
src.error(std::string{"a `"} + (char)cmt_op[1] + (char)cmt_op[2] + "` comment closed by `" + (char)cmt_cl[0] +
"`");
}
comm >>= 1;
src.advance(1);
} else {
src.advance(1);
}
if (comm < 0) {
src.error("too many nested comments");
}
}
if (src.seek_eof()) {
eof = true;
if (comm > 1) {
if (comm & 1) {
src.error(std::string{"`"} + (char)cmt_op[1] + (char)cmt_op[2] + "` comment extends past end of file");
} else {
src.error(std::string{"`"} + (char)cmt_op[0] + "` comment extends past end of file");
}
}
return lexem.clear(src.here(), Lexem::Eof);
}
int c = src.cur_char();
const char* end = src.get_ptr();
if (is_quote_char(c) || c == '`') {
int qc = c;
++end;
while (end < src.get_end_ptr() && *end != qc) {
++end;
}
if (*end != qc) {
src.error(qc == '`' ? "a `back-quoted` token extends past end of line" : "string extends past end of line");
}
lexem.set(std::string{src.get_ptr() + 1, end}, src.here(), qc == '`' ? Lexem::Unknown : Lexem::String);
src.set_ptr(end + 1);
// std::cerr << lexem.name_str() << ' ' << lexem.str << std::endl;
return lexem;
}
int len = 0, pc = -0x100;
while (end < src.get_end_ptr()) {
c = *end;
bool repeated = (c == pc && is_repeatable(c));
if (c == ' ' || c == 9 || (len && is_left_active(c) && !repeated)) {
break;
}
++len;
++end;
if (is_right_active(c) && !repeated) {
break;
}
pc = c;
}
lexem.set(std::string{src.get_ptr(), end}, src.here());
src.set_ptr(end);
// std::cerr << lexem.name_str() << ' ' << lexem.str << std::endl;
return lexem;
}
const Lexem& Lexer::peek() {
if (peek_lexem.valid()) {
return peek_lexem;
}
if (eof) {
return lexem.clear(src.here(), Lexem::Eof);
}
Lexem keep = std::move(lexem);
next();
peek_lexem = std::move(lexem);
lexem = std::move(keep);
eof = false;
return peek_lexem;
}
} // namespace src

114
crypto/parser/lexer.h Normal file
View file

@ -0,0 +1,114 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "srcread.h"
#include <array>
#include <memory>
#include <cstring>
namespace src {
/*
*
* LEXER
*
*/
int lexem_is_special(std::string str); // return 0 if no special lexems are needed
struct Lexem {
enum { Undefined = -2, Eof = -1, Unknown = 0, Ident = 0, Number = 1, Special = 2, String = 3 };
int tp;
int val;
std::string str;
SrcLocation loc;
int classify();
Lexem(std::string _str = "", const SrcLocation& _loc = {}, int _tp = Unknown, int _val = 0)
: tp(_tp), val(_val), str(_str), loc(_loc) {
classify();
}
int set(std::string _str = "", const SrcLocation& _loc = {}, int _tp = Unknown, int _val = 0);
Lexem& clear(const SrcLocation& _loc = {}, int _tp = Unknown, int _val = 0) {
tp = _tp;
val = _val;
loc = _loc;
str = "";
return *this;
}
bool valid() const {
return tp != Undefined;
}
std::string name_str() const;
void error(std::string _str) const {
throw ParseError{loc, _str};
}
void error_at(std::string str1, std::string str2) const {
error(str1 + str + str2);
}
static std::string lexem_name_str(int idx);
};
class Lexer {
SourceReader& src;
bool eof;
Lexem lexem, peek_lexem;
unsigned char char_class[128];
std::array<int, 3> eol_cmt, cmt_op, cmt_cl;
enum cc { left_active = 2, right_active = 1, active = 3, allow_repeat = 4, quote_char = 8 };
public:
bool eof_found() const {
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 = "\"");
const Lexem& next();
const Lexem& cur() const {
return lexem;
}
const Lexem& peek();
int tp() const {
return lexem.tp;
}
void expect(int exp_tp, const char* msg = 0);
int classify_char(unsigned c) const {
return c < 0x80 ? char_class[c] : 0;
}
bool is_active(int c) const {
return (classify_char(c) & cc::active) == cc::active;
}
bool is_left_active(int c) const {
return (classify_char(c) & cc::left_active);
}
bool is_right_active(int c) const {
return (classify_char(c) & cc::right_active);
}
bool is_repeatable(int c) const {
return (classify_char(c) & cc::allow_repeat);
}
bool is_quote_char(int c) const {
return (classify_char(c) & cc::quote_char);
}
private:
void set_spec(std::array<int, 3>& arr, std::string setup);
};
} // namespace src

180
crypto/parser/srcread.cpp Normal file
View file

@ -0,0 +1,180 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "srcread.h"
namespace src {
/*
*
* SOURCE FILE READER
*
*/
std::ostream& operator<<(std::ostream& os, const FileDescr* fdescr) {
return os << (fdescr ? (fdescr->is_stdin ? "stdin" : fdescr->filename) : "unknown-location");
}
std::ostream& operator<<(std::ostream& os, const Fatal& fatal) {
return os << fatal.get_msg();
}
void SrcLocation::show(std::ostream& os) const {
os << fdescr;
if (line_no > 0) {
os << ':' << line_no;
if (line_pos >= 0) {
os << ':' << (line_pos + 1);
}
}
}
bool SrcLocation::show_context(std::ostream& os) const {
if (text.empty() || line_pos < 0 || (unsigned)line_pos > text.size()) {
return false;
}
bool skip_left = (line_pos > 200), skip_right = (line_pos + 200u < text.size());
const char* start = skip_left ? text.c_str() + line_pos - 100 : text.c_str();
const char* end = skip_right ? text.c_str() + line_pos + 100 : text.c_str() + text.size();
const char* here = text.c_str() + line_pos;
os << " ";
if (skip_left) {
os << "... ";
}
for (const char* ptr = start; ptr < end; ptr++) {
os << (char)*ptr;
}
if (skip_right) {
os << " ...";
}
os << std::endl;
os << " ";
if (skip_left) {
os << "... ";
}
for (const char* ptr = start; ptr < here; ptr++) {
char c = *ptr;
os << (c == 9 || c == 10 ? c : ' ');
}
os << '^' << std::endl;
return true;
}
std::ostream& operator<<(std::ostream& os, const SrcLocation& loc) {
loc.show(os);
return os;
}
void SrcLocation::show_gen_error(std::ostream& os, std::string message, std::string err_type) const {
show(os);
if (!err_type.empty()) {
os << ": " << err_type;
}
os << ": " << message << std::endl;
show_context(os);
}
std::ostream& operator<<(std::ostream& os, const Error& error) {
error.show(os);
return os;
}
void ParseError::show(std::ostream& os) const {
os << where << ": error: " << message << std::endl;
where.show_context(os);
}
SourceReader::SourceReader(std::istream* _is, const FileDescr* _fdescr)
: ifs(_is), loc(_fdescr), eof(false), cur_line_len(0), start(0), cur(0), end(0) {
load_line();
}
void SourceReader::set_eof() {
if (!eof) {
eof = true;
start = cur = end = 0;
}
}
int SourceReader::skip_spc() {
if (!cur) {
return 0;
}
const char* ptr = cur;
int res = 0;
while (*ptr == ' ' || *ptr == 9) {
++ptr;
++res;
}
set_ptr(ptr);
return res;
}
bool SourceReader::seek_eof() {
while (seek_eoln()) {
if (!load_line()) {
return true;
}
}
return false;
}
const char* SourceReader::set_ptr(const char* ptr) {
if (ptr != cur) {
if (ptr < cur || ptr > end) {
error("parsing position went outside of line");
}
loc.line_pos = (int)(ptr - start);
cur = ptr;
}
return ptr;
}
bool SourceReader::load_line() {
if (eof) {
return false;
}
if (ifs->eof()) {
set_eof();
return false;
}
++loc.line_no;
loc.line_pos = -1;
std::getline(*ifs, cur_line);
if (ifs->fail()) {
set_eof();
if (!ifs->eof()) {
error("cannot read line from source stream");
}
return false;
}
std::size_t len = cur_line.size();
if (len > 0xffffff) {
set_eof();
error("line too long");
return false;
}
loc.text = cur_line;
cur_line_len = (int)len;
loc.line_pos = 0;
cur = start = cur_line.c_str();
end = start + cur_line_len;
return true;
}
} // namespace src

150
crypto/parser/srcread.h Normal file
View file

@ -0,0 +1,150 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <string>
#include <iostream>
namespace src {
/*
*
* SOURCE FILE READER
*
*/
struct FileDescr {
std::string filename;
bool is_stdin;
FileDescr(std::string _fname, bool _stdin = false) : filename(std::move(_fname)), is_stdin(_stdin) {
}
};
struct Fatal {
std::string message;
Fatal(std::string _msg) : message(std::move(_msg)) {
}
std::string get_msg() const {
return message;
}
};
std::ostream& operator<<(std::ostream& os, const Fatal& fatal);
struct SrcLocation {
const FileDescr* fdescr;
int line_no;
int line_pos;
std::string text;
SrcLocation() : fdescr(nullptr), line_no(0), line_pos(-1) {
}
SrcLocation(const FileDescr* _fdescr, int line = 0, int pos = -1) : fdescr(_fdescr), line_no(line), line_pos(pos) {
}
bool defined() const {
return fdescr;
}
void show(std::ostream& os) const;
bool show_context(std::ostream& os) const;
void show_gen_error(std::ostream& os, std::string message, std::string err_type = "") const;
void show_note(std::string err_msg) const {
show_gen_error(std::cerr, err_msg, "note");
}
void show_warning(std::string err_msg) const {
show_gen_error(std::cerr, err_msg, "warning");
}
void show_error(std::string err_msg) const {
show_gen_error(std::cerr, err_msg, "error");
}
};
std::ostream& operator<<(std::ostream& os, const SrcLocation& loc);
struct Error {
virtual ~Error() = default;
virtual void show(std::ostream& os) const = 0;
};
std::ostream& operator<<(std::ostream& os, const Error& error);
struct ParseError : Error {
SrcLocation where;
std::string message;
ParseError(const SrcLocation& _where, std::string _msg) : where(_where), message(_msg) {
}
ParseError(const SrcLocation* _where, std::string _msg) : message(_msg) {
if (_where) {
where = *_where;
}
}
~ParseError() override = default;
void show(std::ostream& os) const override;
};
class SourceReader {
std::istream* ifs;
SrcLocation loc;
bool eof;
std::string cur_line;
int cur_line_len;
void set_eof();
const char *start, *cur, *end;
public:
SourceReader(std::istream* _is, const FileDescr* _fdescr);
bool load_line();
bool is_eof() const {
return eof;
}
int is_eoln() const {
return cur == end;
}
int skip_spc();
bool seek_eoln() {
skip_spc();
return is_eoln();
}
bool seek_eof();
const char* cur_line_cstr() const {
return cur_line.c_str();
}
const SrcLocation& here() const {
return loc;
}
char cur_char() const {
return *cur;
}
char next_char() const {
return cur[1];
}
const char* get_ptr() const {
return cur;
}
const char* get_end_ptr() const {
return end;
}
const char* set_ptr(const char* ptr);
void advance(int n) {
set_ptr(get_ptr() + n);
}
void error(std::string err_msg) {
throw ParseError{loc, err_msg};
}
};
} // namespace src

181
crypto/parser/symtable.cpp Normal file
View file

@ -0,0 +1,181 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "symtable.h"
#include <sstream>
#include <cassert>
namespace sym {
/*
*
* SYMBOL VALUES (DECLARED)
*
*/
int scope_level;
SymTable<100003> symbols;
SymDef* sym_def[symbols.hprime];
SymDef* global_sym_def[symbols.hprime];
std::vector<std::pair<int, SymDef>> symbol_stack;
std::vector<src::SrcLocation> scope_opened_at;
std::string Symbol::unknown_symbol_name(sym_idx_t i) {
if (!i) {
return "_";
} else {
std::ostringstream os;
os << "SYM#" << i;
return os.str();
}
}
sym_idx_t SymTableBase::gen_lookup(std::string str, int mode, sym_idx_t idx) {
unsigned long long h1 = 1, h2 = 1;
for (char c : str) {
h1 = ((h1 * 239) + (unsigned char)(c)) % p;
h2 = ((h2 * 17) + (unsigned char)(c)) % (p - 1);
}
++h2;
++h1;
while (true) {
if (sym_table[h1]) {
if (sym_table[h1]->str == str) {
return (mode & 2) ? not_found : sym_idx_t(h1);
}
h1 += h2;
if (h1 > p) {
h1 -= p;
}
} else {
if (!(mode & 1)) {
return not_found;
}
if (def_sym >= ((long)p * 3) / 4) {
throw SymTableOverflow{def_sym};
}
sym_table[h1] = std::make_unique<Symbol>(str, idx <= 0 ? sym_idx_t(h1) : -idx);
++def_sym;
return sym_idx_t(h1);
}
}
}
SymTableBase& SymTableBase::add_keyword(std::string str, sym_idx_t idx) {
if (idx <= 0) {
idx = ++def_kw;
}
sym_idx_t res = gen_lookup(str, -1, idx);
if (!res) {
throw SymTableKwRedef{str};
}
if (idx < max_kw_idx) {
keywords[idx] = res;
}
return *this;
}
void open_scope(src::Lexer& lex) {
++scope_level;
scope_opened_at.push_back(lex.cur().loc);
}
void close_scope(src::Lexer& lex) {
if (!scope_level) {
throw src::Fatal{"cannot close the outer scope"};
}
while (!symbol_stack.empty() && symbol_stack.back().first == scope_level) {
SymDef old_def = symbol_stack.back().second;
auto idx = old_def.sym_idx;
symbol_stack.pop_back();
SymDef* cur_def = sym_def[idx];
assert(cur_def);
assert(cur_def->level == scope_level && cur_def->sym_idx == idx);
//std::cerr << "restoring local symbol `" << old_def.name << "` of level " << scope_level << " to its previous level " << old_def.level << std::endl;
if (cur_def->value) {
//std::cerr << "deleting value of symbol " << old_def.name << ":" << old_def.level << " at " << (const void*) it->second.value << std::endl;
delete cur_def->value;
}
if (!old_def.level && !old_def.value) {
delete cur_def; // ??? keep the definition always?
sym_def[idx] = nullptr;
} else {
cur_def->value = std::move(old_def.value);
cur_def->level = old_def.level;
}
old_def.value = nullptr;
}
--scope_level;
scope_opened_at.pop_back();
}
SymDef* lookup_symbol(sym_idx_t idx, int flags) {
if (!idx) {
return nullptr;
}
if ((flags & 1) && sym_def[idx]) {
return sym_def[idx];
}
if ((flags & 2) && global_sym_def[idx]) {
return global_sym_def[idx];
}
return nullptr;
}
SymDef* lookup_symbol(std::string name, int flags) {
return lookup_symbol(symbols.lookup(name), flags);
}
SymDef* define_global_symbol(sym_idx_t name_idx, bool force_new, const src::SrcLocation& loc) {
if (!name_idx) {
return nullptr;
}
auto found = global_sym_def[name_idx];
if (found) {
return force_new && found->value ? nullptr : found;
}
return global_sym_def[name_idx] = new SymDef(0, name_idx, loc);
}
SymDef* define_symbol(sym_idx_t name_idx, bool force_new, const src::SrcLocation& loc) {
if (!name_idx) {
return nullptr;
}
if (!scope_level) {
return define_global_symbol(name_idx, force_new, loc);
}
auto found = sym_def[name_idx];
if (found) {
if (found->level < scope_level) {
symbol_stack.push_back(std::make_pair(scope_level, *found));
found->level = scope_level;
} else if (found->value && force_new) {
return nullptr;
}
found->value = 0;
found->loc = loc;
return found;
}
found = sym_def[name_idx] = new SymDef(scope_level, name_idx, loc);
symbol_stack.push_back(std::make_pair(scope_level, SymDef{0, name_idx}));
return found;
}
} // namespace sym

Some files were not shown because too many files have changed in this diff Show more