mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
initial commit
This commit is contained in:
commit
c2da007f40
1610 changed files with 398047 additions and 0 deletions
333
crypto/CMakeLists.txt
Normal file
333
crypto/CMakeLists.txt
Normal 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
394
crypto/Ed25519.cpp
Normal 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
72
crypto/Ed25519.h
Normal 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
493
crypto/block/Binlog.cpp
Normal 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
157
crypto/block/Binlog.h
Normal 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
111
crypto/block/block-binlog.h
Normal 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
|
||||
135
crypto/block/block-db-impl.h
Normal file
135
crypto/block/block-db-impl.h
Normal 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
835
crypto/block/block-db.cpp
Normal 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
164
crypto/block/block-db.h
Normal 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
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
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
1923
crypto/block/block.cpp
Normal file
File diff suppressed because it is too large
Load diff
569
crypto/block/block.h
Normal file
569
crypto/block/block.h
Normal 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
662
crypto/block/block.tlb
Normal 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;
|
||||
294
crypto/block/check-proof.cpp
Normal file
294
crypto/block/check-proof.cpp
Normal 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
|
||||
80
crypto/block/check-proof.h
Normal file
80
crypto/block/check-proof.h
Normal 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
|
||||
780
crypto/block/create-state.cpp
Normal file
780
crypto/block/create-state.cpp
Normal 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
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
617
crypto/block/mc-config.h
Normal 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
|
||||
221
crypto/block/output-queue-merger.cpp
Normal file
221
crypto/block/output-queue-merger.cpp
Normal 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
|
||||
80
crypto/block/output-queue-merger.h
Normal file
80
crypto/block/output-queue-merger.h
Normal 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
248
crypto/block/test-block.cpp
Normal 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
2089
crypto/block/transaction.cpp
Normal file
File diff suppressed because it is too large
Load diff
401
crypto/block/transaction.h
Normal file
401
crypto/block/transaction.h
Normal 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
200
crypto/common/AtomicRef.h
Normal 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
41
crypto/common/bigint.cpp
Normal 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
2525
crypto/common/bigint.hpp
Normal file
File diff suppressed because it is too large
Load diff
668
crypto/common/bitstring.cpp
Normal file
668
crypto/common/bitstring.cpp
Normal 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
662
crypto/common/bitstring.h
Normal 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
55
crypto/common/refcnt.cpp
Normal 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
474
crypto/common/refcnt.hpp
Normal 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
321
crypto/common/refint.cpp
Normal 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
127
crypto/common/refint.h
Normal 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
212
crypto/common/util.cpp
Normal 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
42
crypto/common/util.h
Normal 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
280
crypto/ellcurve/Ed25519.cpp
Normal 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
188
crypto/ellcurve/Ed25519.h
Normal 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
|
||||
33
crypto/ellcurve/Fp25519.cpp
Normal file
33
crypto/ellcurve/Fp25519.cpp
Normal 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
32
crypto/ellcurve/Fp25519.h
Normal 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
|
||||
138
crypto/ellcurve/Montgomery.cpp
Normal file
138
crypto/ellcurve/Montgomery.cpp
Normal 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
|
||||
123
crypto/ellcurve/Montgomery.h
Normal file
123
crypto/ellcurve/Montgomery.h
Normal 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
|
||||
255
crypto/ellcurve/TwEdwards.cpp
Normal file
255
crypto/ellcurve/TwEdwards.cpp
Normal 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
145
crypto/ellcurve/TwEdwards.h
Normal 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
200
crypto/fift/Dictionary.cpp
Normal 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
182
crypto/fift/Dictionary.h
Normal 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
76
crypto/fift/Fift.cpp
Normal 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
53
crypto/fift/Fift.h
Normal 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
192
crypto/fift/IntCtx.cpp
Normal 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
147
crypto/fift/IntCtx.h
Normal 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
|
||||
89
crypto/fift/SourceLookup.cpp
Normal file
89
crypto/fift/SourceLookup.cpp
Normal 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
|
||||
71
crypto/fift/SourceLookup.h
Normal file
71
crypto/fift/SourceLookup.h
Normal 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
221
crypto/fift/fift-main.cpp
Normal 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
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
83
crypto/fift/lib/Fift.fif
Normal 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
436
crypto/fift/lib/Lisp.fif
Normal 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
184
crypto/fift/lib/Lists.fif
Normal 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
266
crypto/fift/lib/Stack.fif
Normal 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
125
crypto/fift/lib/TonUtil.fif
Normal 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
175
crypto/fift/utils.cpp
Normal 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
35
crypto/fift/utils.h
Normal 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
2747
crypto/fift/words.cpp
Normal file
File diff suppressed because it is too large
Load diff
43
crypto/fift/words.h
Normal file
43
crypto/fift/words.h
Normal 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
492
crypto/func/abscode.cpp
Normal 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
846
crypto/func/analyzer.cpp
Normal 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
329
crypto/func/asmops.cpp
Normal 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
974
crypto/func/builtins.cpp
Normal 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
689
crypto/func/codegen.cpp
Normal 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
248
crypto/func/func.cpp
Normal 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
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
325
crypto/func/gen-abscode.cpp
Normal 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
115
crypto/func/keywords.cpp
Normal 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
539
crypto/func/optimize.cpp
Normal 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
1105
crypto/func/parse-func.cpp
Normal file
File diff suppressed because it is too large
Load diff
826
crypto/func/stack-transform.cpp
Normal file
826
crypto/func/stack-transform.cpp
Normal 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
85
crypto/func/test/a6.fc
Normal 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
75
crypto/func/test/a6.fp
Normal 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
6
crypto/func/test/a6_1.fc
Normal 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
130
crypto/func/test/a6_2.fc
Normal 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
12
crypto/func/test/a7.fc
Normal 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
28
crypto/func/test/a8.fc
Normal 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
23
crypto/func/test/a9.fc
Normal 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
28
crypto/func/test/a9_1.fc
Normal 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
63
crypto/func/test/b1.fc
Normal 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
60
crypto/func/test/b2.fc
Normal 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
63
crypto/func/test/b2_0.fc
Normal 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
62
crypto/func/test/b2_1.fc
Normal 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
58
crypto/func/test/b2_2.fc
Normal 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
384
crypto/func/unify-types.cpp
Normal 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
256
crypto/openssl/bignum.cpp
Normal 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
368
crypto/openssl/bignum.h
Normal 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
151
crypto/openssl/digest.h
Normal 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
122
crypto/openssl/rand.cpp
Normal 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
56
crypto/openssl/rand.hpp
Normal 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
176
crypto/openssl/residue.cpp
Normal 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
189
crypto/openssl/residue.h
Normal 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
288
crypto/parser/lexer.cpp
Normal 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
114
crypto/parser/lexer.h
Normal 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
180
crypto/parser/srcread.cpp
Normal 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
150
crypto/parser/srcread.h
Normal 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
181
crypto/parser/symtable.cpp
Normal 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
Loading…
Add table
Add a link
Reference in a new issue