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

initial commit

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

88
.clang-format Normal file
View file

@ -0,0 +1,88 @@
---
Language: Cpp
# BasedOnStyle: Google
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: true
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None # All
AllowShortIfStatementsOnASingleLine: false # true
AllowShortLoopsOnASingleLine: false # true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true # false
# BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 120 # 80
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: true
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
# ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IndentCaseLabels: true
IndentWidth: 2
IndentWrappedFunctionNames: false
# JavaScriptQuotes: Leave
# JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
# ObjCBlockIndentWidth: 2
# ObjCSpaceAfterProperty: false
# ObjCSpaceBeforeProtocolList: false
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
ReflowComments: false # true
SortIncludes: false # disabled, because we need case insensitive sort
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 100 # 8
UseTab: Never
...

12
.clang_complete Normal file
View file

@ -0,0 +1,12 @@
-xc++
-std=c++14
-iquote .
-iquote tdtl/
-iquote tl/
-iquote tl/generate/
-iquote tdactor/
-iquote tdnet/
-iquote tdutils/
-iquote ../ton-build/tdutils/
-iquote crypto/
-I/opt/local/include/

36
.gitattributes vendored Normal file
View file

@ -0,0 +1,36 @@
* text=auto
*.cpp text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.hpp text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.h text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.c text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.fc text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.fif text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.tl text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.tlb text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.txt text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.sh text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent eol=lf
*.cmake text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.in text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.tex text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.py text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.ans text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.bat text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent eol=crlf
*.fp text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.gradle text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.java text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.kt text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.md text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.php text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.pro text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.properties text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.ps1 text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent eol=crlf
*.xml text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.yml text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
third_party/** linguist-vendored
*.a binary
*.jar binary
*.tlo binary

13
.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
__pycache__
parser.out
parsetab.py
parsetab.pyc
tdutils/generate/auto/
tl/generate/auto/
compile_commands.json
crypto/block/block-auto.cpp
crypto/block/block-auto.h
crypto/smartcont/*-code.fif
test/regression-tests.cache/
*.swp
**/*build*/

12
.gitmodules vendored Normal file
View file

@ -0,0 +1,12 @@
[submodule "third-party/rocksdb"]
path = third-party/rocksdb
url = https://github.com/facebook/rocksdb.git
[submodule "third-party/crc32c"]
path = third-party/crc32c
url = https://github.com/google/crc32c
[submodule "third-party/abseil-cpp"]
path = third-party/abseil-cpp
url = https://github.com/abseil/abseil-cpp.git
[submodule "third-party/libraptorq"]
path = third-party/libraptorq
url = https://github.com/ton-blockchain/libRaptorQ

View file

@ -0,0 +1,74 @@
# - Adds a compiler flag if it is supported by the compiler
#
# This function checks that the supplied compiler flag is supported and then
# adds it to the corresponding compiler flags
#
# add_cxx_compiler_flag(<FLAG> [<VARIANT>])
#
# - Example
#
# include(AddCXXCompilerFlag)
# add_cxx_compiler_flag(-Wall)
# add_cxx_compiler_flag(-no-strict-aliasing RELEASE)
# Requires CMake 2.6+
if (__add_cxx_compiler_flag)
return()
endif()
set(__add_cxx_compiler_flag INCLUDED)
include(CheckCXXCompilerFlag)
function(mangle_compiler_flag FLAG OUTPUT)
string(TOUPPER "HAVE_CXX_FLAG_${FLAG}" SANITIZED_FLAG)
string(REPLACE "+" "X" SANITIZED_FLAG ${SANITIZED_FLAG})
string(REGEX REPLACE "[^A-Za-z_0-9]" "_" SANITIZED_FLAG ${SANITIZED_FLAG})
string(REGEX REPLACE "_+" "_" SANITIZED_FLAG ${SANITIZED_FLAG})
set(${OUTPUT} "${SANITIZED_FLAG}" PARENT_SCOPE)
endfunction(mangle_compiler_flag)
function(add_cxx_compiler_flag FLAG)
string(REPLACE "-Wno-" "-W" MAIN_FLAG ${FLAG})
mangle_compiler_flag("${MAIN_FLAG}" MANGLED_FLAG_NAME)
if (DEFINED CMAKE_REQUIRED_FLAGS)
set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}")
else()
set(CMAKE_REQUIRED_FLAGS "${FLAG}")
endif()
check_cxx_compiler_flag("${MAIN_FLAG}" ${MANGLED_FLAG_NAME})
if (DEFINED OLD_CMAKE_REQUIRED_FLAGS)
set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
else()
unset(CMAKE_REQUIRED_FLAGS)
endif()
if (${MANGLED_FLAG_NAME})
set(VARIANT ${ARGV1})
if (ARGV1)
string(TOUPPER "_${VARIANT}" VARIANT)
endif()
set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE)
endif()
endfunction()
function(add_required_cxx_compiler_flag FLAG)
string(REPLACE "-Wno-" "-W" MAIN_FLAG ${FLAG})
mangle_compiler_flag("${MAIN_FLAG}" MANGLED_FLAG_NAME)
set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}")
check_cxx_compiler_flag("${MAIN_FLAG}" ${MANGLED_FLAG_NAME})
set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
if (${MANGLED_FLAG_NAME})
set(VARIANT ${ARGV1})
if (ARGV1)
string(TOUPPER "_${VARIANT}" VARIANT)
endif()
set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}" PARENT_SCOPE)
else()
message(FATAL_ERROR "Required flag '${FLAG}' is not supported by the compiler")
endif()
endfunction()

21
CMake/FindJeMalloc.cmake Normal file
View file

@ -0,0 +1,21 @@
# - Find JeMalloc library
# Find the native JeMalloc includes and library
#
# JEMALLOC_INCLUDE_DIR - where to find jemalloc.h, etc.
# JEMALLOC_LIBRARIES - List of libraries when using jemalloc.
# JEMALLOC_FOUND - True if jemalloc found.
find_path(JEMALLOC_INCLUDE_DIR
NAMES jemalloc/jemalloc.h
HINTS ${JEMALLOC_ROOT_DIR}/include)
find_library(JEMALLOC_LIBRARIES
NAMES jemalloc
HINTS ${JEMALLOC_ROOT_DIR}/lib)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(jemalloc DEFAULT_MSG JEMALLOC_LIBRARIES JEMALLOC_INCLUDE_DIR)
mark_as_advanced(
JEMALLOC_LIBRARIES
JEMALLOC_INCLUDE_DIR)

38
CMake/FindMHD.cmake Normal file
View file

@ -0,0 +1,38 @@
# - Try to find MHD
# Once done this will define
#
# MHD_FOUND - system has MHD
# MHD_INCLUDE_DIRS - the MHD include directory
# MHD_LIBRARY - Link these to use MHD
find_path(
MHD_INCLUDE_DIR
NAMES microhttpd.h
DOC "microhttpd include dir"
)
find_library(
MHD_LIBRARY
NAMES microhttpd microhttpd-10 libmicrohttpd libmicrohttpd-dll
DOC "microhttpd library"
)
set(MHD_INCLUDE_DIRS ${MHD_INCLUDE_DIR})
set(MHD_LIBRARIES ${MHD_LIBRARY})
# debug library on windows
# same naming convention as in qt (appending debug library with d)
# boost is using the same "hack" as us with "optimized" and "debug"
# official MHD project actually uses _d suffix
if (MSVC)
find_library(
MHD_LIBRARY_DEBUG
NAMES microhttpd_d microhttpd-10_d libmicrohttpd_d libmicrohttpd-dll_d
DOC "mhd debug library"
)
set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG})
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(mhd DEFAULT_MSG MHD_INCLUDE_DIR MHD_LIBRARY)
mark_as_advanced(MHD_INCLUDE_DIR MHD_LIBRARY)

25
CMake/FindReadline.cmake Normal file
View file

@ -0,0 +1,25 @@
if (APPLE)
find_path(READLINE_INCLUDE_DIR readline/readline.h /usr/local/opt/readline/include /opt/local/include /opt/include /usr/local/include /usr/include NO_DEFAULT_PATH)
endif()
find_path(READLINE_INCLUDE_DIR readline/readline.h)
if (APPLE)
find_library(READLINE_LIBRARY readline /usr/local/opt/readline/lib /opt/local/lib /opt/lib /usr/local/lib /usr/lib NO_DEFAULT_PATH)
endif()
find_library(READLINE_LIBRARY readline)
if (READLINE_INCLUDE_DIR AND READLINE_LIBRARY AND NOT GNU_READLINE_FOUND)
set(CMAKE_REQUIRED_INCLUDES "${READLINE_INCLUDE_DIR}")
set(CMAKE_REQUIRED_LIBRARIES "${READLINE_LIBRARY}")
include(CheckCXXSourceCompiles)
unset(GNU_READLINE_FOUND CACHE)
check_cxx_source_compiles("#include <stdio.h>\n#include <readline/readline.h>\nint main() { rl_replace_line(\"\", 0); }" GNU_READLINE_FOUND)
if (NOT GNU_READLINE_FOUND)
unset(READLINE_INCLUDE_DIR CACHE)
unset(READLINE_LIBRARY CACHE)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Readline DEFAULT_MSG READLINE_INCLUDE_DIR READLINE_LIBRARY)
mark_as_advanced(READLINE_INCLUDE_DIR READLINE_LIBRARY)

1936
CMake/UseLATEX.cmake Normal file

File diff suppressed because it is too large Load diff

452
CMakeLists.txt Normal file
View file

@ -0,0 +1,452 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
project(TON VERSION 0.5 LANGUAGES C CXX)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
#set(OPENSSL_USE_STATIC_LIBS TRUE)
# Prevent in-source build
get_filename_component(TON_REAL_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" REALPATH)
get_filename_component(TON_REAL_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}" REALPATH)
if (TON_REAL_BINARY_DIR STREQUAL TON_REAL_SOURCE_DIR)
message(" Out-of-source build should be used to build TDLib.")
message(" You need to remove the files already created by CMake and")
message(" rerun CMake from a new directory:")
message(" rm -rf CMakeFiles CMakeCache.txt")
message(" mkdir build")
message(" cd build")
message(" cmake ..")
message(FATAL_ERROR "In-source build failed.")
endif()
#BEGIN internal
option(TON_USE_ROCKSDB "Use \"ON\" to enable RocksDb." ON)
option(TON_USE_ABSEIL "Use \"ON\" to enable Abseil." ON)
option(TON_USE_JEMALLOC "Use \"ON\" to enable JeMalloc." OFF)
#END internal
option(TONLIB_ENABLE_JNI "Use \"ON\" to enable JNI-compatible TonLib API.")
option(TON_USE_ASAN "Use \"ON\" to enable AddressSanitizer." OFF)
option(TON_USE_TSAN "Use \"ON\" to enable ThreadSanitizer." OFF)
option(TON_USE_UBSAN "Use \"ON\" to enable UndefinedBehaviorSanitizer." OFF)
set(TON_ARCH "native" CACHE STRING "Architecture, will be passed to -march=")
if (TON_USE_ABSEIL)
message("Add abseil-cpp")
add_subdirectory(third-party/abseil-cpp EXCLUDE_FROM_ALL)
set(ABSL_FOUND 1)
endif()
#add_subdirectory(third-party/libcuckoo EXCLUDE_FROM_ALL)
#add_subdirectory(third-party/junction EXCLUDE_FROM_ALL)
if (WIN32)
message("Add wingetopt")
add_subdirectory(third-party/wingetopt EXCLUDE_FROM_ALL)
set(WINGETOPT_FOUND 1)
message(STATUS "Use wingetopt")
endif()
set(CRC32C_BUILD_TESTS OFF CACHE BOOL "Build CRC32C's unit tests")
set(CRC32C_BUILD_BENCHMARKS OFF CACHE BOOL "Build CRC32C's benchmarks")
set(CRC32C_USE_GLOG OFF CACHE BOOL "Build CRC32C's tests with Google Logging")
set(CRC32C_INSTALL OFF CACHE BOOL "Install CRC32C's header and library")
message("Add crc32c")
add_subdirectory(third-party/crc32c EXCLUDE_FROM_ALL)
set(CRC32C_FOUND 1)
if (TON_USE_ROCKSDB)
if (ANDROID)
set(PORTABLE ON CACHE BOOL "portable")
endif()
set(WITH_TESTS OFF CACHE BOOL "build with tests")
set(WITH_TOOLS OFF CACHE BOOL "build with tools")
set(FAIL_ON_WARNINGS OFF CACHE BOOL "fail on warnings")
message("Add rocksdb")
add_subdirectory(third-party/rocksdb EXCLUDE_FROM_ALL)
endif()
option(USE_COROUTINES "experimental support of coroutines" OFF)
if (USE_COROUTINES)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(TD_HAVE_COROUTINES 1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines-ts")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
endif()
option(USE_LIBRAPTORQ "use libraptorq for tests" OFF)
if (USE_LIBRAPTORQ)
set(USE_LZ4 OFF CACHE BOOL "use lz4")
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CLANG_STDLIB "ON") # for libraptorq
endif()
message("Add libraptorq")
add_subdirectory(third-party/libraptorq EXCLUDE_FROM_ALL)
endif()
message("Add ton")
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH})
# Configure CCache if available
find_program(CCACHE_FOUND ccache)
#set(CCACHE_FOUND 0)
if (CCACHE_FOUND)
message(STATUS "Found ccache")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
else()
message(STATUS "Could NOT find ccache")
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(GCC 1)
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CLANG 1)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
set(INTEL 1)
elseif (NOT MSVC)
message(FATAL_ERROR "Compiler isn't supported")
endif()
include(CheckCXXCompilerFlag)
if (GCC OR CLANG OR INTEL)
if (WIN32 AND INTEL)
set(STD14_FLAG /Qstd=c++14)
else()
set(STD14_FLAG -std=c++14)
endif()
check_cxx_compiler_flag(${STD14_FLAG} HAVE_STD14)
if (NOT HAVE_STD14)
string(REPLACE "c++14" "c++1y" STD14_FLAG "${STD14_FLAG}")
check_cxx_compiler_flag(${STD14_FLAG} HAVE_STD1Y)
set(HAVE_STD14 ${HAVE_STD1Y})
endif()
elseif (MSVC)
set(HAVE_STD14 MSVC_VERSION>=1900)
endif()
if (NOT HAVE_STD14)
message(FATAL_ERROR "No C++14 support in the compiler. Please upgrade the compiler.")
endif()
set(CMAKE_THREAD_PREFER_PTHREAD ON)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
if (TON_ARCH AND NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${TON_ARCH}")
endif()
if (THREADS_HAVE_PTHREAD_ARG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
endif()
if (TON_USE_JEMALLOC)
find_package(JeMalloc REQUIRED)
endif()
set(MEMPROF "" CACHE STRING "Use one of \"ON\", \"FAST\" or \"SAFE\" to enable memory profiling. \
Works under macOS and Linux when compiled using glibc. \
In FAST mode stack is unwinded only using frame pointers, which may fail. \
In SAFE mode stack is unwinded using backtrace function from execinfo.h, which may be very slow. \
By default both methods are used to achieve maximum speed and accuracy")
if (CLANG OR GCC)
if (MEMPROF)
check_cxx_compiler_flag(-no-pie CXX_NO_PIE_FLAG)
if (CXX_NO_PIE_FLAG)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie")
elseif (APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-no_pie")
endif()
endif()
endif()
if (MSVC)
if (CMAKE_CXX_FLAGS_DEBUG MATCHES "/RTC1")
string(REPLACE "/RTC1" " " CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
endif()
add_definitions(-D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /W4 /wd4100 /wd4127 /wd4324 /wd4456 /wd4457 /wd4458 /wd4505 /wd4702")
elseif (CLANG OR GCC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${STD14_FLAG} -fno-omit-frame-pointer")
if (APPLE)
#use "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/export_list" for exported symbols
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fvisibility=hidden -Wl,-dead_strip,-x,-S")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fvisibility=hidden -Wl,-dead_strip,-x,-S")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,--exclude-libs,ALL")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
if (NOT TON_USE_ASAN AND NOT TON_USE_TSAN AND NOT MEMPROF)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--exclude-libs,ALL")
endif()
endif()
elseif (INTEL)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${STD14_FLAG}")
endif()
if (WIN32)
add_definitions(-DNTDDI_VERSION=0x06020000 -DWINVER=0x0602 -D_WIN32_WINNT=0x0602 -DNOMINMAX -DUNICODE -D_UNICODE)
endif()
if (CYGWIN)
add_definitions(-D_DEFAULT_SOURCE=1 -DFD_SETSIZE=4096)
endif()
if (NOT ANDROID) # _FILE_OFFSET_BITS is broken in ndk r15 and r15b and doesn't work prior to Android 7.0
add_definitions(-D_FILE_OFFSET_BITS=64)
endif()
include(AddCXXCompilerFlag)
if (MSVC)
add_cxx_compiler_flag("/experimental:external /external:anglebrackets /external:W0")
endif()
if (NOT MSVC)
add_cxx_compiler_flag("-Wall")
endif()
add_cxx_compiler_flag("-Wextra")
add_cxx_compiler_flag("-Wimplicit-fallthrough=2")
add_cxx_compiler_flag("-Wpointer-arith")
add_cxx_compiler_flag("-Wcast-qual")
add_cxx_compiler_flag("-Wsign-compare")
add_cxx_compiler_flag("-Wduplicated-branches")
add_cxx_compiler_flag("-Wduplicated-cond")
add_cxx_compiler_flag("-Walloc-zero")
add_cxx_compiler_flag("-Wlogical-op")
add_cxx_compiler_flag("-Wno-tautological-compare")
add_cxx_compiler_flag("-Wpointer-arith")
add_cxx_compiler_flag("-Wvla")
add_cxx_compiler_flag("-Wnon-virtual-dtor")
add_cxx_compiler_flag("-Wno-unused-parameter")
add_cxx_compiler_flag("-Wconversion")
add_cxx_compiler_flag("-Wno-sign-conversion")
add_cxx_compiler_flag("-Qunused-arguments")
add_cxx_compiler_flag("-Wno-unused-private-field")
add_cxx_compiler_flag("-Wno-redundant-move")
#add_cxx_compiler_flag("-Werror")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem /usr/include/c++/v1")
if (CLANG)
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
if (TON_USE_ASAN)
if (CLANG)
add_cxx_compiler_flag("-stdlib=libc++")
endif()
add_cxx_compiler_flag("-fsanitize=address")
add_definitions(-DTD_USE_ASAN=1)
endif()
if (TON_USE_TSAN)
if (CLANG)
add_cxx_compiler_flag("-stdlib=libc++")
endif()
add_cxx_compiler_flag("-fsanitize=thread")
endif()
if (TON_USE_UBSAN)
if (CLANG)
add_cxx_compiler_flag("-stdlib=libc++")
endif()
add_cxx_compiler_flag("-fsanitize=undefined")
endif()
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=leak")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -finstrument-functions")
#Compilation database
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
#BEGIN internal
find_package(LATEX)
if (LATEX_FOUND)
include(UseLATEX)
add_latex_document(doc/ton.tex TARGET_NAME ton_white_paper)
add_latex_document(doc/tvm.tex TARGET_NAME ton_vm_description)
add_latex_document(doc/tblkch.tex TARGET_NAME ton_blockchain_description)
add_latex_document(doc/fiftbase.tex TARGET_NAME fift_basic_description)
endif()
#END internal
function(target_link_libraries_system target)
set(libs ${ARGN})
foreach(lib ${libs})
get_target_property(lib_include_dirs ${lib} INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(${target} SYSTEM PUBLIC ${lib_include_dirs})
target_link_libraries(${target} PUBLIC ${lib})
endforeach(lib)
endfunction(target_link_libraries_system)
add_subdirectory(tdutils)
add_subdirectory(memprof)
add_subdirectory(tdactor)
add_subdirectory(tdnet)
if (TON_USE_ROCKSDB)
option(TDDB_USE_ROCKSDB "Use rockdb" ON)
endif()
add_subdirectory(tddb)
add_subdirectory(tdtl)
add_subdirectory(tl)
add_subdirectory(terminal)
add_subdirectory(keys)
add_subdirectory(tl-utils)
add_subdirectory(adnl)
add_subdirectory(crypto)
add_subdirectory(lite-client)
#BEGIN tonlib
add_subdirectory(tonlib)
#END tonlib
#BEGIN internal
add_subdirectory(common)
add_subdirectory(tdfec)
add_subdirectory(keyring)
add_subdirectory(fec)
add_subdirectory(rldp)
add_subdirectory(dht)
add_subdirectory(overlay)
add_subdirectory(catchain)
add_subdirectory(validator-session)
add_subdirectory(validator)
add_subdirectory(blockchain-explorer)
add_subdirectory(validator-engine)
add_subdirectory(validator-engine-console)
add_subdirectory(dht-server)
add_subdirectory(utils)
#END internal
if (NOT CMAKE_CROSSCOMPILING)
add_custom_target(prepare_cross_compiling DEPENDS tl_generate_common tdmime_auto tlb_generate_block)
endif()
#TESTS
add_executable(test-ed25519 test/test-td-main.cpp ${ED25519_TEST_SOURCE})
target_link_libraries(test-ed25519 PRIVATE ton_crypto)
add_executable(test-vm test/test-td-main.cpp ${TONVM_TEST_SOURCE})
target_link_libraries(test-vm PRIVATE ton_crypto fift-lib)
add_executable(test-cells test/test-td-main.cpp ${CELLS_TEST_SOURCE})
target_link_libraries(test-cells PRIVATE ton_crypto)
add_executable(test-fift test/test-td-main.cpp ${FIFT_TEST_SOURCE})
target_link_libraries(test-fift PRIVATE fift-lib)
add_executable(test-tdutils test/test-td-main.cpp ${TDUTILS_TEST_SOURCE})
target_link_libraries(test-tdutils PRIVATE tdutils ${CMAKE_THREAD_LIBS_INIT} memprof ${JEMALLOC_LIBRARIES})
#target_link_libraries_system(test-tdutils absl::base absl::container absl::hash )
#target_link_libraries_system(test-tdutils libcuckoo)
#target_include_directories(test-tdutils PRIVATE SYSTEM ${JUNCTION_ALL_INCLUDE_DIRS})
#target_link_libraries(test-tdutils PRIVATE ${JUNCTION_ALL_LIBRARIES})
add_executable(test-tdactor test/test-td-main.cpp ${TDACTOR_TEST_SOURCE})
target_link_libraries(test-tdactor PRIVATE tdactor ${CMAKE_THREAD_LIBS_INIT})
add_executable(test-net test/test-td-main.cpp ${NET_TEST_SOURCE})
target_link_libraries(test-net PRIVATE tdnet tdutils ${CMAKE_THREAD_LIBS_INIT})
#BEGIN tonlib
add_executable(test-tonlib ${TONLIB_ONLINE_TEST_SOURCE})
target_link_libraries(test-tonlib tdutils tdactor adnllite tl_api ton_crypto ton_block tl_tonlib_api tonlib)
add_executable(test-tonlib-offline test/test-td-main.cpp ${TONLIB_OFFLINE_TEST_SOURCE})
target_link_libraries(test-tonlib-offline tdutils tdactor adnllite tl_api ton_crypto ton_block fift-lib tl_tonlib_api tonlib)
#END tonlib
#BEGIN internal
add_executable(test-db test/test-td-main.cpp ${TONDB_TEST_SOURCE})
target_link_libraries(test-db PRIVATE ton_db memprof)
add_executable(test-tddb test/test-td-main.cpp ${TDDB_TEST_SOURCE})
target_link_libraries(test-tddb PRIVATE tdutils tddb ${CMAKE_THREAD_LIBS_INIT} memprof)
add_executable(test-fec test/test-td-main.cpp ${FEC_TEST_SOURCE})
target_link_libraries(test-fec PRIVATE tdfec tdutils ${CMAKE_THREAD_LIBS_INIT})
if (USE_LIBRAPTORQ)
target_link_libraries(test-fec PRIVATE third_party_fec)
target_compile_definitions(test-fec PRIVATE "USE_LIBRAPTORQ=1")
endif()
add_executable(test-hello-world test/test-hello-world.cpp )
target_link_libraries(test-hello-world tl_api)
add_executable(test-adnl test/test-adnl.cpp)
target_link_libraries(test-adnl adnl adnltest dht tl_api)
add_executable(test-dht test/test-dht.cpp)
target_link_libraries(test-dht adnl adnltest dht tl_api)
add_executable(test-rldp test/test-rldp.cpp)
target_link_libraries(test-rldp adnl adnltest dht rldp tl_api)
add_executable(test-validator-session-state test/test-validator-session-state.cpp)
target_link_libraries(test-validator-session-state adnl dht rldp validatorsession tl_api)
#add_executable(test-node test/test-node.cpp)
#target_link_libraries(test-node overlay tdutils tdactor adnl tl_api dht
# catchain validatorsession)
add_executable(test-catchain test/test-catchain.cpp)
target_link_libraries(test-catchain overlay tdutils tdactor adnl adnltest rldp tl_api dht
catchain )
#add_executable(test-validator-session test/test-validator-session.cpp)
#target_link_libraries(test-validator-session overlay tdutils tdactor adnl tl_api dht
# catchain validatorsession)
#add_executable(test-ton-dummy-0 test/test-ton-dummy-0.cpp)
#target_link_libraries(test-ton-dummy-0 overlay tdutils tdactor adnl tl_api dht
# catchain validatorsession ton-node validator dummy_validator validator )
#add_executable(test-dummy-0-lite-client test/test-dummy-0-lite-client.cpp)
#target_link_libraries(test-dummy-0-lite-client overlay tdutils tdactor adnl tl_api dht
# catchain validatorsession ton-node validator dummy_validator validator
# terminal )
#add_executable(test-ton-dummy-0-collator test/test-ton-collator.cpp)
#target_link_libraries(test-ton-dummy-0-collator overlay tdutils tdactor adnl tl_api
# dht catchain validatorsession ton-node validator_disk dummy_validator validator_disk )
add_executable(test-ton-collator test/test-ton-collator.cpp)
target_link_libraries(test-ton-collator overlay tdutils tdactor adnl tl_api dht
catchain validatorsession validator-disk ton_validator validator-disk )
#add_executable(test-validator test/test-validator.cpp)
#target_link_libraries(test-validator overlay tdutils tdactor adnl tl_api dht
# rldp catchain validatorsession ton-node validator ton_validator validator memprof ${JEMALLOC_LIBRARIES})
#add_executable(test-ext-server test/test-ext-server.cpp)
#target_link_libraries(test-ext-server tdutils tdactor adnl tl_api dht )
#add_executable(test-ext-client test/test-ext-client.cpp)
#target_link_libraries(test-ext-client tdutils tdactor adnl tl_api tl-lite-utils)
get_directory_property(HAS_PARENT PARENT_DIRECTORY)
if (HAS_PARENT)
set(ALL_TEST_SOURCE
${TDUTILS_TEST_SOURCE}
${TDACTOR_TEST_SOURCE}
${NET_TEST_SOURCE}
${TDDB_TEST_SOURCE}
${FEC_TEST_SOURCE}
${ED25519_TEST_SOURCE}
${TONDB_TEST_SOURCE}
${CELLS_TEST_SOURCE} # ${TONVM_TEST_SOURCE} ${FIFT_TEST_SOURCE} ${TONLIB_ONLINE_TEST_SOURCE}
PARENT_SCOPE)
endif()
add_library(all_tests INTERFACE)
target_link_libraries(all_tests INTERFACE tdutils tdactor tdnet tdfec ton_db ton_crypto fift-lib)
#END internal
enable_testing()
set(TEST_OPTIONS "--regression ${CMAKE_CURRENT_SOURCE_DIR}/test/regression-tests.ans --filter -Bench")
separate_arguments(TEST_OPTIONS)
add_test(test-ed25519 test-ed25519)
add_test(test-vm test-vm ${TEST_OPTIONS})
add_test(test-fift test-fift ${TEST_OPTIONS})
add_test(test-cells test-cells ${TEST_OPTIONS})
add_test(test-net test-net)
add_test(test-actors test-tdactor)
#BEGIN tonlib
add_test(test-tdutils test-tdutils)
add_test(test-tonlib-offline test-tonlib-offline)
#END tonlib
#BEGIN internal
add_test(test-fec test-fec)
add_test(test-tddb test-tddb ${TEST_OPTIONS})
add_test(test-db test-db ${TEST_OPTIONS})
#END internal

27
GPLv2 Normal file
View file

@ -0,0 +1,27 @@
/*
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
*/

18
LGPLv2 Normal file
View file

@ -0,0 +1,18 @@
/*
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
*/

481
LICENSE.LGPL Normal file
View file

@ -0,0 +1,481 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

98
adnl/CMakeLists.txt Normal file
View file

@ -0,0 +1,98 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
#BEGIN internal
set(ADNL_HEADERS
adnl-address-list.h
adnl-address-list.hpp
adnl-db.h
adnl-db.hpp
adnl-channel.h
adnl-channel.hpp
adnl-ext-client.h
adnl-ext-client.hpp
adnl-ext-connection.hpp
adnl-ext-server.h
adnl-ext-server.hpp
adnl-local-id.h
adnl-message.h
adnl-network-manager.h
adnl-network-manager.hpp
adnl-node.h
adnl-packet.h
adnl-peer-table.h
adnl-peer-table.hpp
adnl-peer.h
adnl-peer.hpp
adnl-query.h
adnl-static-nodes.h
adnl-static-nodes.hpp
adnl-proxy-types.h
adnl-proxy-types.hpp
adnl.h
utils.hpp
)
set(ADNL_SOURCE
adnl-address-list.cpp
adnl-db.cpp
adnl-ext-client.cpp
adnl-ext-server.cpp
adnl-ext-connection.cpp
adnl-local-id.cpp
adnl-message.cpp
adnl-network-manager.cpp
adnl-node.cpp
adnl-packet.cpp
adnl-peer-table.cpp
adnl-peer.cpp
adnl-query.cpp
adnl-channel.cpp
adnl-static-nodes.cpp
adnl-proxy-types.cpp
utils.cpp
${ADNL_HEADERS}
)
set(ADNL_TEST_SOURCE
adnl-test-loopback-implementation.h
adnl-test-loopback-implementation.cpp
)
set(ADNL_PROXY_SOURCE
adnl-proxy.cpp
adnl-proxy-types.h
adnl-proxy-types.hpp
adnl-proxy-types.cpp
)
#FIXME
set(ADNL_LITE_HEADERS ${ADNL_HEADERS})
#END internal
set(ADNL_LITE_SOURCE
adnl-ext-client.cpp
adnl-ext-connection.cpp
adnl-query.cpp
${ADNL_LITE_HEADERS}
)
#BEGIN internal
add_library(adnl STATIC ${ADNL_SOURCE})
target_include_directories(adnl PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(adnl PUBLIC tdactor ton_crypto tl_api tdnet tddb keys keyring )
add_executable(adnl-proxy ${ADNL_PROXY_SOURCE})
target_include_directories(adnl-proxy PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(adnl-proxy PUBLIC tdactor ton_crypto tl_api tdnet common
tl-utils)
add_library(adnltest STATIC ${ADNL_TEST_SOURCE})
target_include_directories(adnltest PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(adnltest PUBLIC adnl )
#END internal
add_library(adnllite STATIC ${ADNL_LITE_SOURCE})
target_include_directories(adnllite PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(adnllite PUBLIC tdactor ton_crypto tl_lite_api tdnet keys )

160
adnl/adnl-address-list.cpp Normal file
View file

@ -0,0 +1,160 @@
/*
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 "adnl-address-list.hpp"
#include "adnl-peer-table.h"
#include "auto/tl/ton_api.hpp"
#include "td/utils/overloaded.h"
#include "td/net/UdpServer.h"
namespace ton {
namespace adnl {
class AdnlNetworkConnectionUdp : public AdnlNetworkConnection {
public:
void send(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::uint32 priority, td::BufferSlice message) override;
bool is_alive() const override {
return true;
}
bool is_active() const override {
return true;
}
void start_up() override {
callback_->on_change_state(true);
}
AdnlNetworkConnectionUdp(td::actor::ActorId<AdnlNetworkManager> network_manager, td::uint32 ip, td::uint16 port,
std::unique_ptr<AdnlNetworkConnection::Callback> callback);
AdnlNetworkConnectionUdp(td::actor::ActorId<AdnlNetworkManager> network_manager, td::Bits128 ip, td::uint16 port,
std::unique_ptr<AdnlNetworkConnection::Callback> callback);
private:
td::actor::ActorId<AdnlNetworkManager> network_manager_;
td::IPAddress addr_;
std::unique_ptr<AdnlNetworkConnection::Callback> callback_;
};
void AdnlNetworkConnectionUdp::send(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::uint32 priority,
td::BufferSlice message) {
LOG_CHECK(message.size() <= AdnlNetworkManager::get_mtu()) << "dst=" << addr_ << " size=" << message.size();
td::actor::send_closure(network_manager_, &AdnlNetworkManager::send_udp_packet, src, dst, addr_, priority,
std::move(message));
}
AdnlNetworkConnectionUdp::AdnlNetworkConnectionUdp(td::actor::ActorId<AdnlNetworkManager> network_manager,
td::uint32 ip, td::uint16 port,
std::unique_ptr<AdnlNetworkConnection::Callback> callback)
: network_manager_(network_manager), callback_(std::move(callback)) {
addr_.init_host_port(td::IPAddress::ipv4_to_str(ip), port).ensure();
}
AdnlNetworkConnectionUdp::AdnlNetworkConnectionUdp(td::actor::ActorId<AdnlNetworkManager> network_manager,
td::Bits128 ip, td::uint16 port,
std::unique_ptr<AdnlNetworkConnection::Callback> callback)
: network_manager_(network_manager), callback_(std::move(callback)) {
addr_.init_host_port(td::IPAddress::ipv6_to_str(ip.as_slice()), port).ensure();
}
AdnlAddressImpl::Hash AdnlAddressImpl::get_hash() const {
return get_tl_object_sha_bits256(tl());
}
td::actor::ActorOwn<AdnlNetworkConnection> AdnlAddressUdp::create_connection(
td::actor::ActorId<AdnlNetworkManager> network_manager,
std::unique_ptr<AdnlNetworkConnection::Callback> callback) const {
return td::actor::create_actor<AdnlNetworkConnectionUdp>("udpconn", network_manager, ip_, port_, std::move(callback));
}
AdnlAddressUdp::AdnlAddressUdp(const ton_api::adnl_address_udp &obj) {
ip_ = obj.ip_;
port_ = static_cast<td::uint16>(obj.port_);
}
td::actor::ActorOwn<AdnlNetworkConnection> AdnlAddressUdp6::create_connection(
td::actor::ActorId<AdnlNetworkManager> network_manager,
std::unique_ptr<AdnlNetworkConnection::Callback> callback) const {
return td::actor::create_actor<AdnlNetworkConnectionUdp>("udpconn", network_manager, ip_, port_, std::move(callback));
}
AdnlAddressUdp6::AdnlAddressUdp6(const ton_api::adnl_address_udp6 &obj) {
ip_ = obj.ip_;
port_ = static_cast<td::uint16>(obj.port_);
}
td::Ref<AdnlAddressImpl> AdnlAddressImpl::create(const tl_object_ptr<ton_api::adnl_Address> &addr) {
td::Ref<AdnlAddressImpl> res = td::Ref<AdnlAddressImpl>{};
ton_api::downcast_call(*const_cast<ton_api::adnl_Address *>(addr.get()),
td::overloaded(
[&](const ton_api::adnl_address_udp &obj) {
res = td::Ref<AdnlAddressUdp>{true, obj};
},
[&](const ton_api::adnl_address_udp6 &obj) {
res = td::Ref<AdnlAddressUdp6>{true, obj};
}));
return res;
}
bool AdnlAddressList::public_only() const {
for (auto &addr : addrs_) {
if (!addr->is_public()) {
return false;
}
}
return true;
}
AdnlAddressList::AdnlAddressList(const tl_object_ptr<ton_api::adnl_addressList> &addrs) {
version_ = static_cast<td::uint32>(addrs->version_);
std::vector<td::Ref<AdnlAddressImpl>> vec;
for (auto &addr : addrs->addrs_) {
vec.push_back(AdnlAddressImpl::create(addr));
}
addrs_ = std::move(vec);
reinit_date_ = addrs->reinit_date_;
priority_ = addrs->priority_;
expire_at_ = addrs->expire_at_;
}
tl_object_ptr<ton_api::adnl_addressList> AdnlAddressList::tl() const {
std::vector<tl_object_ptr<ton_api::adnl_Address>> addrs;
for (auto &v : addrs_) {
addrs.emplace_back(v->tl());
}
return create_tl_object<ton_api::adnl_addressList>(std::move(addrs), version_, reinit_date_, priority_, expire_at_);
}
td::uint32 AdnlAddressList::serialized_size() const {
td::uint32 res = 24;
for (auto &addr : addrs_) {
res += addr->serialized_size();
}
return res;
}
td::Result<AdnlAddressList> AdnlAddressList::create(const tl_object_ptr<ton_api::adnl_addressList> &addr_list) {
auto A = AdnlAddressList{addr_list};
if (A.serialized_size() > max_serialized_size()) {
return td::Status::Error(ErrorCode::protoviolation, PSTRING() << "too big addr list: size=" << A.serialized_size());
}
return A;
}
} // namespace adnl
} // namespace ton

105
adnl/adnl-address-list.h Normal file
View file

@ -0,0 +1,105 @@
/*
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 "adnl-network-manager.h"
#include "crypto/common/refcnt.hpp"
namespace ton {
namespace adnl {
class AdnlAddressImpl : public td::CntObject {
public:
using Hash = td::Bits256;
virtual ~AdnlAddressImpl() = default;
virtual Hash get_hash() const;
virtual bool is_public() const = 0;
virtual td::uint32 serialized_size() const = 0;
virtual tl_object_ptr<ton_api::adnl_Address> tl() const = 0;
virtual td::actor::ActorOwn<AdnlNetworkConnection> create_connection(
td::actor::ActorId<AdnlNetworkManager> network_manager,
std::unique_ptr<AdnlNetworkConnection::Callback> callback) const = 0;
static td::Ref<AdnlAddressImpl> create(const tl_object_ptr<ton_api::adnl_Address> &addr);
};
using AdnlAddress = td::Ref<AdnlAddressImpl>;
class AdnlAddressList {
private:
AdnlAddressList(const tl_object_ptr<ton_api::adnl_addressList> &addrs);
td::int32 version_;
td::int32 reinit_date_;
td::int32 priority_;
td::int32 expire_at_;
std::vector<AdnlAddress> addrs_;
public:
static constexpr td::uint32 max_serialized_size() {
return 128;
}
const auto &addrs() const {
return addrs_;
}
auto version() const {
return version_;
}
auto reinit_date() const {
return reinit_date_;
}
auto priority() const {
return priority_;
}
auto expire_at() const {
return expire_at_;
}
void set_version(td::uint32 version) {
version_ = version;
}
void set_reinit_date(td::int32 date) {
reinit_date_ = date;
}
void set_expire_at(td::int32 date) {
expire_at_ = date;
}
bool empty() const {
return version_ == -1;
}
void add_addr(AdnlAddress addr) {
addrs_.push_back(addr);
}
bool public_only() const;
td::uint32 size() const {
return static_cast<td::uint32>(addrs_.size());
}
td::uint32 serialized_size() const;
tl_object_ptr<ton_api::adnl_addressList> tl() const;
AdnlAddressList() : version_{-1}, reinit_date_{0}, priority_{0}, expire_at_{0} {
}
static td::Result<AdnlAddressList> create(const tl_object_ptr<ton_api::adnl_addressList> &addr_list);
};
} // namespace adnl
} // namespace ton

View file

@ -0,0 +1,90 @@
/*
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 "adnl-node-id.hpp"
#include "adnl-address-list.h"
namespace ton {
namespace adnl {
class AdnlAddressUdp : public AdnlAddressImpl {
private:
td::uint32 ip_;
td::uint16 port_;
public:
explicit AdnlAddressUdp(const ton_api::adnl_address_udp &obj);
AdnlAddressUdp(td::uint32 ip, td::uint16 port) : ip_(ip), port_(port) {
}
AdnlAddressUdp *make_copy() const override {
return new AdnlAddressUdp{ip_, port_};
}
bool is_public() const override {
return true;
}
td::uint32 serialized_size() const override {
return 12;
}
tl_object_ptr<ton_api::adnl_Address> tl() const override {
return create_tl_object<ton_api::adnl_address_udp>(ip_, port_);
}
td::actor::ActorOwn<AdnlNetworkConnection> create_connection(
td::actor::ActorId<AdnlNetworkManager> network_manager,
std::unique_ptr<AdnlNetworkConnection::Callback> callback) const override;
};
class AdnlAddressUdp6 : public AdnlAddressImpl {
private:
td::Bits128 ip_;
td::uint16 port_;
public:
explicit AdnlAddressUdp6(const ton_api::adnl_address_udp6 &obj);
AdnlAddressUdp6(td::Bits128 ip, td::uint16 port) : ip_(ip), port_(port) {
}
AdnlAddressUdp6 *make_copy() const override {
return new AdnlAddressUdp6{ip_, port_};
}
bool is_public() const override {
return true;
}
td::uint32 serialized_size() const override {
return 12;
}
tl_object_ptr<ton_api::adnl_Address> tl() const override {
return create_tl_object<ton_api::adnl_address_udp6>(ip_, port_);
}
td::actor::ActorOwn<AdnlNetworkConnection> create_connection(
td::actor::ActorId<AdnlNetworkManager> network_manager,
std::unique_ptr<AdnlNetworkConnection::Callback> callback) const override;
};
} // namespace adnl
} // namespace ton

129
adnl/adnl-channel.cpp Normal file
View file

@ -0,0 +1,129 @@
/*
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 "adnl-channel.hpp"
#include "adnl-peer.h"
#include "adnl-peer-table.h"
#include "td/utils/crypto.h"
#include "crypto/Ed25519.h"
namespace ton {
namespace adnl {
td::Result<td::actor::ActorOwn<AdnlChannel>> AdnlChannel::create(privkeys::Ed25519 pk_data, pubkeys::Ed25519 pub_data,
AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id,
AdnlChannelIdShort &out_id, AdnlChannelIdShort &in_id,
td::actor::ActorId<AdnlPeerPair> peer_pair) {
td::Ed25519::PublicKey pub_k = pub_data.export_key();
td::Ed25519::PrivateKey priv_k = pk_data.export_key();
TRY_RESULT_PREFIX(shared_secret, td::Ed25519::compute_shared_secret(pub_k, priv_k),
"failed to compute channel shared secret: ");
CHECK(shared_secret.length() == 32);
td::SecureString rev_secret{32};
for (td::uint32 i = 0; i < 32; i++) {
rev_secret.as_mutable_slice()[i] = shared_secret[31 - i];
}
auto R = [&]() -> std::pair<PrivateKey, PublicKey> {
if (local_id < peer_id) {
return {privkeys::AES{std::move(shared_secret)}, pubkeys::AES{std::move(rev_secret)}};
} else if (peer_id < local_id) {
return {privkeys::AES{std::move(rev_secret)}, pubkeys::AES{std::move(shared_secret)}};
} else {
auto c = shared_secret.copy();
return {privkeys::AES{std::move(c)}, pubkeys::AES{std::move(shared_secret)}};
}
}();
in_id = AdnlChannelIdShort{R.first.compute_short_id()};
out_id = AdnlChannelIdShort{R.second.compute_short_id()};
TRY_RESULT_PREFIX(encryptor, R.second.create_encryptor(), "failed to init channel encryptor: ");
TRY_RESULT_PREFIX(decryptor, R.first.create_decryptor(), "failed to init channel decryptor: ");
return td::actor::create_actor<AdnlChannelImpl>("channel", local_id, peer_id, peer_pair, in_id, out_id,
std::move(encryptor), std::move(decryptor));
}
AdnlChannelImpl::AdnlChannelImpl(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id,
td::actor::ActorId<AdnlPeerPair> peer_pair, AdnlChannelIdShort in_id,
AdnlChannelIdShort out_id, std::unique_ptr<Encryptor> encryptor,
std::unique_ptr<Decryptor> decryptor) {
local_id_ = local_id;
peer_id_ = peer_id;
encryptor_ = std::move(encryptor);
decryptor_ = std::move(decryptor);
channel_in_id_ = in_id;
channel_out_id_ = out_id;
peer_pair_ = peer_pair;
VLOG(ADNL_INFO) << this << ": created";
}
void AdnlChannelImpl::decrypt(td::BufferSlice raw_data, td::Promise<AdnlPacket> promise) {
TRY_RESULT_PROMISE_PREFIX(promise, data, decryptor_->decrypt(raw_data.as_slice()),
"failed to decrypt channel message: ");
TRY_RESULT_PROMISE_PREFIX(promise, tl_packet, fetch_tl_object<ton_api::adnl_packetContents>(std::move(data), true),
"decrypted channel packet contains invalid TL scheme: ");
TRY_RESULT_PROMISE_PREFIX(promise, packet, AdnlPacket::create(std::move(tl_packet)), "received bad packet: ");
if (packet.inited_from_short() && packet.from_short() != peer_id_) {
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "bad channel packet destination"));
return;
}
promise.set_value(std::move(packet));
}
void AdnlChannelImpl::send_message(td::uint32 priority, td::actor::ActorId<AdnlNetworkConnection> conn,
td::BufferSlice data) {
auto E = encryptor_->encrypt(data.as_slice());
if (E.is_error()) {
VLOG(ADNL_ERROR) << this << ": dropping OUT message: can not encrypt: " << E.move_as_error();
return;
}
auto enc = E.move_as_ok();
auto B = td::BufferSlice(enc.size() + 32);
td::MutableSlice S = B.as_slice();
S.copy_from(channel_out_id_.as_slice());
S.remove_prefix(32);
S.copy_from(enc.as_slice());
td::actor::send_closure(conn, &AdnlNetworkConnection::send, local_id_, peer_id_, priority, std::move(B));
}
void AdnlChannelImpl::receive(td::BufferSlice data) {
auto P = td::PromiseCreator::lambda(
[peer = peer_pair_, channel_id = channel_in_id_, id = print_id()](td::Result<AdnlPacket> R) {
if (R.is_error()) {
VLOG(ADNL_WARNING) << id << ": dropping IN message: can not decrypt: " << R.move_as_error();
} else {
td::actor::send_closure(peer, &AdnlPeerPair::receive_packet_from_channel, channel_id, R.move_as_ok());
}
});
decrypt(std::move(data), std::move(P));
}
} // namespace adnl
} // namespace ton

46
adnl/adnl-channel.h Normal file
View file

@ -0,0 +1,46 @@
/*
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 "adnl-local-id.h"
#include "adnl-peer.h"
#include "adnl-peer-table.h"
#include "adnl-network-manager.h"
namespace ton {
namespace adnl {
class AdnlPeerPair;
class AdnlChannel : public td::actor::Actor {
public:
static td::Result<td::actor::ActorOwn<AdnlChannel>> create(privkeys::Ed25519 pk, pubkeys::Ed25519 pub,
AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id,
AdnlChannelIdShort &out_id, AdnlChannelIdShort &in_id,
td::actor::ActorId<AdnlPeerPair> peer_pair);
virtual void receive(td::BufferSlice data) = 0;
virtual void send_message(td::uint32 priority, td::actor::ActorId<AdnlNetworkConnection> conn,
td::BufferSlice data) = 0;
virtual ~AdnlChannel() = default;
};
} // namespace adnl
} // namespace ton

82
adnl/adnl-channel.hpp Normal file
View file

@ -0,0 +1,82 @@
/*
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 "adnl-channel.h"
#include "keys/encryptor.h"
namespace ton {
namespace adnl {
class AdnlPeerPair;
class AdnlChannelImpl : public AdnlChannel {
public:
AdnlChannelImpl(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id, td::actor::ActorId<AdnlPeerPair> peer_pair,
AdnlChannelIdShort in_id, AdnlChannelIdShort out_id, std::unique_ptr<Encryptor> encryptor,
std::unique_ptr<Decryptor> decryptor);
void decrypt(td::BufferSlice data, td::Promise<AdnlPacket> promise);
void receive(td::BufferSlice data) override;
void send_message(td::uint32 priority, td::actor::ActorId<AdnlNetworkConnection> conn, td::BufferSlice data) override;
struct AdnlChannelPrintId {
AdnlChannelIdShort channel_out_id_;
AdnlChannelIdShort channel_in_id_;
AdnlNodeIdShort local_id_;
AdnlNodeIdShort peer_id_;
};
AdnlChannelPrintId print_id() const {
return AdnlChannelPrintId{channel_out_id_, channel_in_id_, local_id_, peer_id_};
}
private:
AdnlChannelIdShort channel_out_id_;
AdnlChannelIdShort channel_in_id_;
AdnlNodeIdShort local_id_;
AdnlNodeIdShort peer_id_;
std::unique_ptr<Encryptor> encryptor_;
std::unique_ptr<Decryptor> decryptor_;
td::actor::ActorId<AdnlPeerPair> peer_pair_;
};
} // namespace adnl
} // namespace ton
namespace td {
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::adnl::AdnlChannelImpl::AdnlChannelPrintId &id) {
sb << "[channel " << id.peer_id_ << "-" << id.local_id_ << " " << id.channel_out_id_ << "-" << id.channel_in_id_
<< "]";
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::adnl::AdnlChannelImpl &channel) {
sb << channel.print_id();
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::adnl::AdnlChannelImpl *channel) {
sb << channel->print_id();
return sb;
}
} // namespace td

82
adnl/adnl-db.cpp Normal file
View file

@ -0,0 +1,82 @@
/*
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 "adnl-db.hpp"
#include "td/db/RocksDb.h"
namespace ton {
namespace adnl {
void AdnlDbImpl::update(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id, AdnlDbItem item,
td::Promise<td::Unit> promise) {
td::BufferSlice b{64};
auto S = b.as_slice();
S.copy_from(local_id.as_slice());
S.remove_prefix(32);
S.copy_from(peer_id.as_slice());
auto obj = create_tl_object<ton_api::adnl_db_node_value>(static_cast<td::int32>(td::Clocks::system()), item.id.tl(),
item.addr_list.tl(), item.priority_addr_list.tl());
kv_->begin_transaction().ensure();
kv_->set(b.as_slice(), serialize_tl_object(obj, true).as_slice()).ensure();
kv_->commit_transaction().ensure();
}
void AdnlDbImpl::get(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id, td::Promise<AdnlDbItem> promise) {
td::BufferSlice b{64};
auto S = b.as_slice();
S.copy_from(local_id.as_slice());
S.remove_prefix(32);
S.copy_from(peer_id.as_slice());
std::string value;
auto R = kv_->get(b.as_slice(), value);
R.ensure();
if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
promise.set_error(td::Status::Error(ErrorCode::notready, "not in db"));
return;
}
auto F = fetch_tl_object<ton_api::adnl_db_node_value>(td::BufferSlice{value}, true);
F.ensure();
auto f = F.move_as_ok();
AdnlDbItem n;
auto id = AdnlNodeIdFull::create(f->id_);
id.ensure();
n.id = id.move_as_ok();
auto addr_list = AdnlAddressList::create(std::move(f->addr_list_));
addr_list.ensure();
n.addr_list = addr_list.move_as_ok();
auto priority_addr_list = AdnlAddressList::create(std::move(f->priority_addr_list_));
priority_addr_list.ensure();
n.priority_addr_list = priority_addr_list.move_as_ok();
promise.set_value(std::move(n));
}
void AdnlDbImpl::start_up() {
kv_ = std::make_shared<td::RocksDb>(td::RocksDb::open(path_).move_as_ok());
}
td::actor::ActorOwn<AdnlDb> AdnlDb::create(std::string path) {
return td::actor::create_actor<AdnlDbImpl>("adnldb", path);
}
} // namespace adnl
} // namespace ton

47
adnl/adnl-db.h Normal file
View file

@ -0,0 +1,47 @@
/*
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/actor/actor.h"
#include "adnl.h"
namespace ton {
namespace adnl {
struct AdnlDbItem {
AdnlNodeIdFull id;
AdnlAddressList addr_list;
AdnlAddressList priority_addr_list;
};
class AdnlDb : public td::actor::Actor {
public:
virtual ~AdnlDb() = default;
virtual void update(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id, AdnlDbItem item,
td::Promise<td::Unit> promise) = 0;
virtual void get(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id, td::Promise<AdnlDbItem> promise) = 0;
static td::actor::ActorOwn<AdnlDb> create(std::string path);
};
} // namespace adnl
} // namespace ton

47
adnl/adnl-db.hpp Normal file
View file

@ -0,0 +1,47 @@
/*
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 "adnl-db.h"
#include "td/db/KeyValue.h"
namespace ton {
namespace adnl {
class AdnlDbImpl : public AdnlDb {
public:
void update(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id, AdnlDbItem item,
td::Promise<td::Unit> promise) override;
void get(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id, td::Promise<AdnlDbItem> promise) override;
void start_up() override;
AdnlDbImpl(std::string path) : path_(path) {
}
private:
std::string path_;
std::shared_ptr<td::KeyValue> kv_;
};
} // namespace adnl
} // namespace ton

181
adnl/adnl-ext-client.cpp Normal file
View file

@ -0,0 +1,181 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "adnl-ext-client.hpp"
#include "adnl-ext-client.h"
namespace ton {
namespace adnl {
void AdnlExtClientImpl::alarm() {
if (is_closing_) {
return;
}
if (conn_.empty() || !conn_.is_alive()) {
next_create_at_ = td::Timestamp::in(10.0);
alarm_timestamp() = next_create_at_;
auto fd = td::SocketFd::open(dst_addr_);
if (fd.is_error()) {
LOG(INFO) << "failed to connect to " << dst_addr_ << ": " << fd.move_as_error();
return;
}
class Cb : public AdnlExtConnection::Callback {
private:
td::actor::ActorId<AdnlExtClientImpl> id_;
public:
void on_ready(td::actor::ActorId<AdnlExtConnection> conn) {
td::actor::send_closure(id_, &AdnlExtClientImpl::conn_ready, conn);
}
void on_close(td::actor::ActorId<AdnlExtConnection> conn) {
td::actor::send_closure(id_, &AdnlExtClientImpl::conn_stopped, conn);
}
Cb(td::actor::ActorId<AdnlExtClientImpl> id) : id_(id) {
}
};
conn_ = td::actor::create_actor<AdnlOutboundConnection>(td::actor::ActorOptions().with_name("outconn").with_poll(),
fd.move_as_ok(), std::make_unique<Cb>(actor_id(this)), dst_,
local_id_, actor_id(this));
}
}
void AdnlExtClientImpl::hangup() {
conn_ = {};
is_closing_ = true;
ref_cnt_--;
try_stop();
}
void AdnlExtClientImpl::try_stop() {
if (is_closing_ && ref_cnt_ == 0 && out_queries_.empty()) {
stop();
}
}
td::Status AdnlOutboundConnection::process_custom_packet(td::BufferSlice &data, bool &processed) {
if (data.size() == 12) {
auto F = fetch_tl_object<ton_api::tcp_pong>(data.clone(), true);
if (F.is_ok()) {
processed = true;
return td::Status::OK();
}
}
if (!local_id_.empty() && nonce_.size() != 0) {
auto F = fetch_tl_object<ton_api::tcp_authentificationNonce>(data.clone(), true);
if (F.is_ok()) {
auto f = F.move_as_ok();
if (f->nonce_.size() == 0 || f->nonce_.size() > 512) {
return td::Status::Error(ErrorCode::protoviolation, "bad nonce size");
}
td::SecureString ss{nonce_.size() + f->nonce_.size()};
ss.as_mutable_slice().copy_from(nonce_.as_slice());
ss.as_mutable_slice().remove_prefix(nonce_.size()).copy_from(f->nonce_.as_slice());
TRY_RESULT(dec, local_id_.create_decryptor());
TRY_RESULT(B, dec->sign(ss.as_slice()));
auto obj =
create_tl_object<ton_api::tcp_authentificationComplete>(local_id_.compute_public_key().tl(), std::move(B));
send(serialize_tl_object(obj, true));
nonce_.clear();
processed = true;
authorization_complete_ = true;
return td::Status::OK();
}
}
return td::Status::OK();
}
void AdnlOutboundConnection::start_up() {
AdnlExtConnection::start_up();
auto X = dst_.pubkey().create_encryptor();
if (X.is_error()) {
LOG(ERROR) << "failed to init encryptor: " << X.move_as_error();
stop();
return;
}
auto enc = X.move_as_ok();
td::BufferSlice d{256};
auto id = dst_.compute_short_id();
auto S = d.as_slice();
S.copy_from(id.as_slice());
S.remove_prefix(32);
S.truncate(256 - 64 - 32);
td::Random::secure_bytes(S);
init_crypto(S);
auto R = enc->encrypt(S);
if (R.is_error()) {
LOG(ERROR) << "failed to encrypt: " << R.move_as_error();
stop();
return;
}
auto data = R.move_as_ok();
LOG_CHECK(data.size() == 256 - 32) << "size=" << data.size();
S = d.as_slice();
S.remove_prefix(32);
CHECK(S.size() == data.size());
S.copy_from(data.as_slice());
send_uninit(std::move(d));
if (!local_id_.empty()) {
nonce_ = td::SecureString{32};
td::Random::secure_bytes(nonce_.as_mutable_slice());
auto obj = create_tl_object<ton_api::tcp_authentificate>(td::BufferSlice{nonce_.as_slice()});
send(serialize_tl_object(obj, true));
}
}
void AdnlExtClientImpl::check_ready(td::Promise<td::Unit> promise) {
if (conn_.empty() || !conn_.is_alive()) {
promise.set_error(td::Status::Error(ErrorCode::notready, "not ready"));
return;
}
td::actor::send_closure(td::actor::ActorId<AdnlExtConnection>{conn_.get()}, &AdnlExtConnection::check_ready_async,
std::move(promise));
}
td::actor::ActorOwn<AdnlExtClient> AdnlExtClient::create(AdnlNodeIdFull dst, td::IPAddress dst_addr,
std::unique_ptr<AdnlExtClient::Callback> callback) {
return td::actor::create_actor<AdnlExtClientImpl>("extclient", std::move(dst), dst_addr, std::move(callback));
}
td::actor::ActorOwn<AdnlExtClient> AdnlExtClient::create(AdnlNodeIdFull dst, PrivateKey local_id,
td::IPAddress dst_addr,
std::unique_ptr<AdnlExtClient::Callback> callback) {
return td::actor::create_actor<AdnlExtClientImpl>("extclient", std::move(dst), std::move(local_id), dst_addr,
std::move(callback));
}
td::Status AdnlOutboundConnection::process_packet(td::BufferSlice data) {
TRY_RESULT(F, fetch_tl_object<lite_api::adnl_message_answer>(std::move(data), true));
td::actor::send_closure(ext_client_, &AdnlExtClientImpl::answer_query, F->query_id_, std::move(F->answer_));
return td::Status::OK();
}
} // namespace adnl
} // namespace ton

48
adnl/adnl-ext-client.h Normal file
View file

@ -0,0 +1,48 @@
/*
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 "adnl-node-id.hpp"
#include "td/utils/port/IPAddress.h"
namespace ton {
namespace adnl {
class AdnlExtClient : public td::actor::Actor {
public:
class Callback {
public:
virtual ~Callback() = default;
virtual void on_ready() = 0;
virtual void on_stop_ready() = 0;
};
virtual ~AdnlExtClient() = default;
virtual void check_ready(td::Promise<td::Unit> promise) = 0;
virtual void send_query(std::string name, td::BufferSlice data, td::Timestamp timeout,
td::Promise<td::BufferSlice> promise) = 0;
static td::actor::ActorOwn<AdnlExtClient> create(AdnlNodeIdFull dst, td::IPAddress dst_addr,
std::unique_ptr<AdnlExtClient::Callback> callback);
static td::actor::ActorOwn<AdnlExtClient> create(AdnlNodeIdFull dst, PrivateKey local_id, td::IPAddress dst_addr,
std::unique_ptr<AdnlExtClient::Callback> callback);
};
} // namespace adnl
} // namespace ton

145
adnl/adnl-ext-client.hpp Normal file
View file

@ -0,0 +1,145 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "auto/tl/lite_api.h"
#include "adnl-ext-connection.hpp"
#include "tl-utils/lite-utils.hpp"
#include "td/utils/Random.h"
#include "adnl-query.h"
#include "keys/encryptor.h"
#include "adnl-ext-client.h"
namespace ton {
namespace adnl {
class AdnlExtClientImpl;
class AdnlOutboundConnection : public AdnlExtConnection {
private:
AdnlNodeIdFull dst_;
PrivateKey local_id_;
td::actor::ActorId<AdnlExtClientImpl> ext_client_;
td::SecureString nonce_;
bool authorization_complete_ = false;
public:
AdnlOutboundConnection(td::SocketFd fd, std::unique_ptr<AdnlExtConnection::Callback> callback, AdnlNodeIdFull dst,
td::actor::ActorId<AdnlExtClientImpl> ext_client)
: AdnlExtConnection(std::move(fd), std::move(callback), true), dst_(std::move(dst)), ext_client_(ext_client) {
}
AdnlOutboundConnection(td::SocketFd fd, std::unique_ptr<AdnlExtConnection::Callback> callback, AdnlNodeIdFull dst,
PrivateKey local_id, td::actor::ActorId<AdnlExtClientImpl> ext_client)
: AdnlExtConnection(std::move(fd), std::move(callback), true)
, dst_(std::move(dst))
, local_id_(local_id)
, ext_client_(ext_client) {
}
td::Status process_packet(td::BufferSlice data) override;
td::Status process_init_packet(td::BufferSlice data) override {
UNREACHABLE();
}
td::Status process_custom_packet(td::BufferSlice &data, bool &processed) override;
void start_up() override;
bool authorized() const override {
return local_id_.empty() ? true : authorization_complete_;
}
};
class AdnlExtClientImpl : public AdnlExtClient {
public:
AdnlExtClientImpl(AdnlNodeIdFull dst_id, td::IPAddress dst_addr, std::unique_ptr<Callback> callback)
: dst_(std::move(dst_id)), dst_addr_(dst_addr), callback_(std::move(callback)) {
}
AdnlExtClientImpl(AdnlNodeIdFull dst_id, PrivateKey local_id, td::IPAddress dst_addr,
std::unique_ptr<Callback> callback)
: dst_(std::move(dst_id)), local_id_(local_id), dst_addr_(dst_addr), callback_(std::move(callback)) {
}
void start_up() override {
alarm_timestamp() = next_create_at_;
}
void conn_stopped(td::actor::ActorId<AdnlExtConnection> conn) {
if (!conn_.empty() && conn_.get() == conn) {
callback_->on_stop_ready();
conn_ = {};
alarm_timestamp() = next_create_at_;
try_stop();
}
}
void conn_ready(td::actor::ActorId<AdnlExtConnection> conn) {
if (!conn_.empty() && conn_.get() == conn) {
callback_->on_ready();
}
}
void check_ready(td::Promise<td::Unit> promise) override;
void send_query(std::string name, td::BufferSlice data, td::Timestamp timeout,
td::Promise<td::BufferSlice> promise) override {
auto P = [SelfId = actor_id(this)](AdnlQueryId id) {
td::actor::send_closure(SelfId, &AdnlExtClientImpl::destroy_query, id);
};
auto q_id = generate_next_query_id();
out_queries_.emplace(q_id, AdnlQuery::create(std::move(promise), std::move(P), name, timeout, q_id));
if (!conn_.empty()) {
auto obj = create_tl_object<lite_api::adnl_message_query>(q_id, std::move(data));
td::actor::send_closure(conn_, &AdnlOutboundConnection::send, serialize_tl_object(obj, true));
}
}
void destroy_query(AdnlQueryId id) {
out_queries_.erase(id);
try_stop();
}
void answer_query(AdnlQueryId id, td::BufferSlice data) {
auto it = out_queries_.find(id);
if (it != out_queries_.end()) {
td::actor::send_closure(it->second, &AdnlQuery::result, std::move(data));
}
}
void alarm() override;
void hangup() override;
AdnlQueryId generate_next_query_id() {
while (true) {
AdnlQueryId q_id = AdnlQuery::random_query_id();
if (out_queries_.count(q_id) == 0) {
return q_id;
}
}
}
private:
AdnlNodeIdFull dst_;
PrivateKey local_id_;
td::IPAddress dst_addr_;
std::unique_ptr<Callback> callback_;
td::actor::ActorOwn<AdnlOutboundConnection> conn_;
td::Timestamp next_create_at_ = td::Timestamp::now_cached();
std::map<AdnlQueryId, td::actor::ActorId<AdnlQuery>> out_queries_;
bool is_closing_{false};
td::uint32 ref_cnt_{1};
void try_stop();
};
} // namespace adnl
} // namespace ton

View file

@ -0,0 +1,192 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "adnl-ext-connection.hpp"
namespace ton {
namespace adnl {
void AdnlExtConnection::send_uninit(td::BufferSlice data) {
buffered_fd_.output_buffer().append(std::move(data));
loop();
}
void AdnlExtConnection::send(td::BufferSlice data) {
LOG(DEBUG) << "sending packet of size " << data.size();
auto data_size = td::narrow_cast<td::uint32>(data.size()) + 32 + 32;
if (data_size < 32 || data_size > (1 << 24)) {
LOG(WARNING) << "bad packet size " << data_size;
return;
}
td::BufferSlice d{data.size() + 4 + 32 + 32};
auto S = d.as_slice();
S.copy_from(td::Slice(reinterpret_cast<const td::uint8 *>(&data_size), 4));
S.remove_prefix(4);
auto Sc = S;
td::Random::secure_bytes(S.copy().truncate(32));
S.remove_prefix(32);
S.copy_from(data.as_slice());
S.remove_prefix(data.size());
td::sha256(Sc.truncate(32 + data.size()), S);
td::BufferSlice e{d.size()};
out_ctr_.encrypt(d.as_slice(), e.as_slice());
buffered_fd_.output_buffer().append(std::move(e));
loop();
}
td::Status AdnlExtConnection::receive(td::ChainBufferReader &input, bool &exit_loop) {
if (stop_read_) {
exit_loop = true;
return td::Status::OK();
}
if (input.size() > 0) {
received_bytes_ = 1;
}
if (inited_) {
if (!read_len_) {
if (input.size() < 4) {
exit_loop = true;
return td::Status::OK();
}
char x[4];
td::MutableSlice s{x, 4};
input.advance(4, s);
td::MutableSlice e{reinterpret_cast<td::uint8 *>(&len_), 4};
in_ctr_.encrypt(s, e);
LOG(DEBUG) << "len=" << len_;
if (len_ > (1 << 24) || len_ < 32) {
return td::Status::Error("Too big packet");
}
read_len_ = true;
}
if (input.size() < len_) {
exit_loop = true;
return td::Status::OK();
}
auto data = input.cut_head(len_).move_as_buffer_slice();
update_timer();
td::BufferSlice dec_data{data.size()};
in_ctr_.encrypt(data.as_slice(), dec_data.as_slice());
exit_loop = false;
read_len_ = false;
len_ = 0;
return receive_packet(std::move(dec_data));
} else {
if (input.size() < 256) {
exit_loop = true;
return td::Status::OK();
}
auto data = input.cut_head(256).move_as_buffer_slice();
update_timer();
exit_loop = false;
return process_init_packet(std::move(data));
}
}
void AdnlExtConnection::loop() {
auto status = [&] {
TRY_STATUS(buffered_fd_.flush_read());
auto &input = buffered_fd_.input_buffer();
bool exit_loop = false;
while (!exit_loop) {
TRY_STATUS(receive(input, exit_loop));
}
TRY_STATUS(buffered_fd_.flush_write());
if (td::can_close(buffered_fd_)) {
stop();
}
return td::Status::OK();
}();
if (status.is_error()) {
LOG(ERROR) << "Client got error " << status;
stop();
} else {
send_ready();
}
}
td::Status AdnlExtConnection::init_crypto(td::Slice S) {
if (S.size() < 96) {
return td::Status::Error(ErrorCode::protoviolation, "too small enc data");
}
CHECK(S.size() >= 96);
td::SecureString s1(32), s2(32);
td::SecureString v1(16), v2(16);
s1.as_mutable_slice().copy_from(S.copy().truncate(32));
S.remove_prefix(32);
s2.as_mutable_slice().copy_from(S.copy().truncate(32));
S.remove_prefix(32);
v1.as_mutable_slice().copy_from(S.copy().truncate(16));
S.remove_prefix(16);
v2.as_mutable_slice().copy_from(S.copy().truncate(16));
S.remove_prefix(16);
if (is_client_) {
in_ctr_.init(s1, v1);
out_ctr_.init(s2, v2);
} else {
in_ctr_.init(s2, v2);
out_ctr_.init(s1, v1);
}
inited_ = true;
return td::Status::OK();
}
td::Status AdnlExtConnection::receive_packet(td::BufferSlice data) {
LOG(DEBUG) << "received packet of size " << data.size();
auto S = data.as_slice();
S.truncate(data.size() - 32);
auto D = data.as_slice();
D.remove_prefix(data.size() - 32);
if (td::sha256(S) != D) {
return td::Status::Error(ErrorCode::protoviolation, "sha256 mismatch");
}
data.truncate(data.size() - 32);
data.confirm_read(32);
if (data.size() == 0) {
// keepalive
return td::Status::OK();
}
bool processed = false;
TRY_STATUS(process_custom_packet(data, processed));
if (processed) {
return td::Status::OK();
}
return process_packet(std::move(data));
}
} // namespace adnl
} // namespace ton

View file

@ -0,0 +1,163 @@
/*
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/net/TcpListener.h"
#include "td/utils/crypto.h"
#include "td/utils/BufferedFd.h"
#include "tl-utils/tl-utils.hpp"
#include "td/utils/Random.h"
#include "common/errorcode.h"
#include <map>
#include <set>
namespace ton {
namespace adnl {
class AdnlExtConnection : public td::actor::Actor, public td::ObserverBase {
public:
class Callback {
public:
virtual ~Callback() = default;
virtual void on_close(td::actor::ActorId<AdnlExtConnection> conn) = 0;
virtual void on_ready(td::actor::ActorId<AdnlExtConnection> conn) = 0;
};
double timeout() {
return is_client_ ? 20.0 : 60.0;
}
AdnlExtConnection(td::SocketFd fd, std::unique_ptr<Callback> callback, bool is_client)
: buffered_fd_(std::move(fd)), callback_(std::move(callback)), is_client_(is_client) {
}
void send(td::BufferSlice data);
void send_uninit(td::BufferSlice data);
td::Status receive(td::ChainBufferReader &input, bool &exit_loop);
virtual td::Status process_packet(td::BufferSlice data) = 0;
td::Status receive_packet(td::BufferSlice data);
virtual td::Status process_custom_packet(td::BufferSlice &data, bool &processed) = 0;
virtual td::Status process_init_packet(td::BufferSlice data) = 0;
virtual bool authorized() const {
return false;
}
td::Status init_crypto(td::Slice data);
void stop_read() {
stop_read_ = true;
}
void resume_read() {
stop_read_ = false;
}
bool check_ready() const {
return received_bytes_ && inited_ && authorized() && !td::can_close(buffered_fd_);
}
void check_ready_async(td::Promise<td::Unit> promise) {
if (check_ready()) {
promise.set_value(td::Unit());
} else {
promise.set_error(td::Status::Error(ErrorCode::notready, "not ready"));
}
}
void send_ready() {
if (check_ready() && !sent_ready_ && callback_) {
callback_->on_ready(actor_id(this));
sent_ready_ = true;
}
}
protected:
td::BufferedFd<td::SocketFd> buffered_fd_;
td::actor::ActorId<AdnlExtConnection> self_;
std::unique_ptr<Callback> callback_;
bool sent_ready_ = false;
bool is_client_;
void notify() override {
// NB: Interface will be changed
td::actor::send_closure_later(self_, &AdnlExtConnection::on_net);
}
void start_up() override {
self_ = actor_id(this);
// Subscribe for socket updates
// NB: Interface will be changed
td::actor::SchedulerContext::get()->get_poll().subscribe(buffered_fd_.get_poll_info().extract_pollable_fd(this),
td::PollFlags::ReadWrite());
update_timer();
notify();
}
private:
td::AesCtrState in_ctr_;
td::AesCtrState out_ctr_;
bool inited_ = false;
bool stop_read_ = false;
bool read_len_ = false;
td::uint32 len_;
td::uint32 received_bytes_ = 0;
td::Timestamp fail_at_;
td::Timestamp send_ping_at_;
bool ping_sent_ = false;
void on_net() {
loop();
}
void tear_down() override {
if (callback_) {
callback_->on_close(actor_id(this));
callback_ = nullptr;
}
// unsubscribe from socket updates
// nb: interface will be changed
td::actor::SchedulerContext::get()->get_poll().unsubscribe(buffered_fd_.get_poll_info().get_pollable_fd_ref());
}
void update_timer() {
fail_at_ = td::Timestamp::in(timeout());
alarm_timestamp() = fail_at_;
if (is_client_) {
ping_sent_ = false;
send_ping_at_ = td::Timestamp::in(timeout() / 2);
alarm_timestamp().relax(send_ping_at_);
}
}
void loop() override;
void alarm() override {
alarm_timestamp() = fail_at_;
if (fail_at_.is_in_past()) {
stop();
} else if (is_client_ && !ping_sent_) {
if (send_ping_at_.is_in_past()) {
auto obj = create_tl_object<ton_api::tcp_ping>(td::Random::fast_uint64());
send(serialize_tl_object(obj, true));
ping_sent_ = true;
} else {
alarm_timestamp().relax(send_ping_at_);
}
}
}
};
} // namespace adnl
} // namespace ton

182
adnl/adnl-ext-server.cpp Normal file
View file

@ -0,0 +1,182 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "adnl-ext-server.hpp"
#include "keys/encryptor.h"
#include "utils.hpp"
namespace ton {
namespace adnl {
td::Status AdnlInboundConnection::process_packet(td::BufferSlice data) {
TRY_RESULT(f, fetch_tl_object<ton_api::adnl_message_query>(std::move(data), true));
auto P =
td::PromiseCreator::lambda([SelfId = actor_id(this), query_id = f->query_id_](td::Result<td::BufferSlice> R) {
if (R.is_error()) {
auto S = R.move_as_error();
LOG(WARNING) << "failed ext query: " << S;
} else {
auto B = create_tl_object<ton_api::adnl_message_answer>(query_id, R.move_as_ok());
td::actor::send_closure(SelfId, &AdnlInboundConnection::send, serialize_tl_object(B, true));
}
});
td::actor::send_closure(peer_table_, &AdnlPeerTable::deliver_query, remote_id_, local_id_, std::move(f->query_),
std::move(P));
return td::Status::OK();
}
td::Status AdnlInboundConnection::process_init_packet(td::BufferSlice data) {
if (data.size() < 32) {
return td::Status::Error(ErrorCode::protoviolation, "too small init packet");
}
local_id_ = AdnlNodeIdShort{data.as_slice().truncate(32)};
data.confirm_read(32);
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
td::actor::send_closure(SelfId, &AdnlInboundConnection::inited_crypto, std::move(R));
});
td::actor::send_closure(ext_server_, &AdnlExtServerImpl::decrypt_init_packet, local_id_, std::move(data),
std::move(P));
stop_read();
return td::Status::OK();
}
void AdnlInboundConnection::inited_crypto(td::Result<td::BufferSlice> R) {
if (R.is_error()) {
LOG(ERROR) << "failed to init crypto: " << R.move_as_error();
stop();
return;
}
auto S = init_crypto(R.move_as_ok().as_slice());
if (S.is_error()) {
LOG(ERROR) << "failed to init crypto (2): " << R.move_as_error();
stop();
return;
}
send(td::BufferSlice());
resume_read();
notify();
}
td::Status AdnlInboundConnection::process_custom_packet(td::BufferSlice &data, bool &processed) {
if (data.size() == 12) {
auto F = fetch_tl_object<ton_api::tcp_ping>(data.clone(), true);
if (F.is_ok()) {
auto f = F.move_as_ok();
auto obj = create_tl_object<ton_api::tcp_pong>(f->random_id_);
send(serialize_tl_object(obj, true));
processed = true;
return td::Status::OK();
}
}
if (1) {
auto F = fetch_tl_object<ton_api::tcp_authentificate>(data.clone(), true);
if (F.is_ok()) {
if (nonce_.size() > 0 || !remote_id_.is_zero()) {
return td::Status::Error(ErrorCode::protoviolation, "duplicate authentificate");
}
auto f = F.move_as_ok();
nonce_ = td::SecureString{f->nonce_.size() + 256};
nonce_.as_mutable_slice().truncate(f->nonce_.size()).copy_from(f->nonce_.as_slice());
td::Random::secure_bytes(nonce_.as_mutable_slice().remove_prefix(f->nonce_.size()));
auto obj = create_tl_object<ton_api::tcp_authentificationNonce>(
td::BufferSlice{nonce_.as_slice().remove_prefix(f->nonce_.size())});
send(serialize_tl_object(obj, true));
processed = true;
return td::Status::OK();
}
}
if (nonce_.size() != 0) {
auto F = fetch_tl_object<ton_api::tcp_authentificationComplete>(data.clone(), true);
if (F.is_ok()) {
auto f = F.move_as_ok();
if (nonce_.size() == 0 || !remote_id_.is_zero()) {
return td::Status::Error(ErrorCode::protoviolation, "duplicate authentificate");
}
auto pub_key = PublicKey{f->key_};
TRY_RESULT(enc, pub_key.create_encryptor());
TRY_STATUS(enc->check_signature(nonce_.as_slice(), f->signature_.as_slice()));
remote_id_ = AdnlNodeIdShort{pub_key.compute_short_id()};
nonce_.clear();
processed = true;
return td::Status::OK();
}
}
return td::Status::OK();
}
void AdnlExtServerImpl::add_tcp_port(td::uint16 port) {
auto it = listeners_.find(port);
if (it != listeners_.end()) {
return;
}
class Callback : public td::TcpListener::Callback {
private:
td::actor::ActorId<AdnlExtServerImpl> id_;
public:
Callback(td::actor::ActorId<AdnlExtServerImpl> id) : id_(id) {
}
void accept(td::SocketFd fd) override {
td::actor::send_closure(id_, &AdnlExtServerImpl::accepted, std::move(fd));
}
};
auto act = td::actor::create_actor<td::TcpListener>(td::actor::ActorOptions().with_name("listener").with_poll(), port,
std::make_unique<Callback>(actor_id(this)));
listeners_.emplace(port, std::move(act));
}
void AdnlExtServerImpl::add_local_id(AdnlNodeIdShort id) {
local_ids_.insert(id);
}
void AdnlExtServerImpl::accepted(td::SocketFd fd) {
td::actor::create_actor<AdnlInboundConnection>(td::actor::ActorOptions().with_name("inconn").with_poll(),
std::move(fd), peer_table_, actor_id(this))
.release();
}
void AdnlExtServerImpl::decrypt_init_packet(AdnlNodeIdShort dst, td::BufferSlice data,
td::Promise<td::BufferSlice> promise) {
auto it = local_ids_.find(dst);
if (it != local_ids_.end()) {
td::actor::send_closure(peer_table_, &AdnlPeerTable::decrypt_message, dst, std::move(data), std::move(promise));
} else {
promise.set_error(td::Status::Error());
}
}
td::actor::ActorOwn<AdnlExtServer> AdnlExtServerCreator::create(td::actor::ActorId<AdnlPeerTable> adnl,
std::vector<AdnlNodeIdShort> ids,
std::vector<td::uint16> ports) {
return td::actor::create_actor<AdnlExtServerImpl>("extserver", adnl, std::move(ids), std::move(ports));
}
} // namespace adnl
} // namespace ton

35
adnl/adnl-ext-server.h Normal file
View file

@ -0,0 +1,35 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "adnl-peer-table.h"
namespace ton {
namespace adnl {
class AdnlExtServerCreator {
public:
static td::actor::ActorOwn<AdnlExtServer> create(td::actor::ActorId<AdnlPeerTable> adnl,
std::vector<AdnlNodeIdShort> ids, std::vector<td::uint16> ports);
};
} // namespace adnl
} // namespace ton

92
adnl/adnl-ext-server.hpp Normal file
View file

@ -0,0 +1,92 @@
/*
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 "adnl-peer-table.h"
#include "td/net/TcpListener.h"
#include "td/utils/crypto.h"
#include "td/utils/BufferedFd.h"
#include "adnl-ext-connection.hpp"
#include "adnl-ext-server.h"
#include <map>
#include <set>
namespace ton {
namespace adnl {
class AdnlExtServerImpl;
class AdnlInboundConnection : public AdnlExtConnection {
public:
AdnlInboundConnection(td::SocketFd fd, td::actor::ActorId<AdnlPeerTable> peer_table,
td::actor::ActorId<AdnlExtServerImpl> ext_server)
: AdnlExtConnection(std::move(fd), nullptr, false), peer_table_(peer_table), ext_server_(ext_server) {
}
td::Status process_packet(td::BufferSlice data) override;
td::Status process_init_packet(td::BufferSlice data) override;
td::Status process_custom_packet(td::BufferSlice &data, bool &processed) override;
void inited_crypto(td::Result<td::BufferSlice> R);
private:
td::actor::ActorId<AdnlPeerTable> peer_table_;
td::actor::ActorId<AdnlExtServerImpl> ext_server_;
AdnlNodeIdShort local_id_;
td::SecureString nonce_;
AdnlNodeIdShort remote_id_ = AdnlNodeIdShort::zero();
};
class AdnlExtServerImpl : public AdnlExtServer {
public:
void add_tcp_port(td::uint16 port) override;
void add_local_id(AdnlNodeIdShort id) override;
void accepted(td::SocketFd fd);
void decrypt_init_packet(AdnlNodeIdShort dst, td::BufferSlice data, td::Promise<td::BufferSlice> promise);
void start_up() override {
for (auto &port : ports_) {
add_tcp_port(port);
}
ports_.clear();
}
AdnlExtServerImpl(td::actor::ActorId<AdnlPeerTable> adnl, std::vector<AdnlNodeIdShort> ids,
std::vector<td::uint16> ports)
: peer_table_(adnl) {
for (auto &id : ids) {
add_local_id(id);
}
for (auto &port : ports) {
ports_.insert(port);
}
}
private:
td::actor::ActorId<AdnlPeerTable> peer_table_;
std::set<AdnlNodeIdShort> local_ids_;
std::set<td::uint16> ports_;
std::map<td::uint16, td::actor::ActorOwn<td::TcpListener>> listeners_;
};
} // namespace adnl
} // namespace ton

279
adnl/adnl-local-id.cpp Normal file
View file

@ -0,0 +1,279 @@
/*
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 "td/utils/crypto.h"
#include "td/utils/Random.h"
#include "adnl-local-id.h"
#include "keys/encryptor.h"
#include "utils.hpp"
namespace ton {
namespace adnl {
AdnlNodeIdFull AdnlLocalId::get_id() const {
return id_;
}
AdnlNodeIdShort AdnlLocalId::get_short_id() const {
return short_id_;
}
AdnlAddressList AdnlLocalId::get_addr_list() const {
CHECK(!addr_list_.empty());
return addr_list_;
}
void AdnlLocalId::receive(td::BufferSlice data) {
auto P = td::PromiseCreator::lambda(
[peer_table = peer_table_, dst = short_id_, id = print_id()](td::Result<AdnlPacket> R) {
if (R.is_error()) {
VLOG(ADNL_WARNING) << id << ": dropping IN message: cannot decrypt: " << R.move_as_error();
} else {
td::actor::send_closure(peer_table, &AdnlPeerTable::receive_decrypted_packet, dst, R.move_as_ok());
}
});
decrypt(std::move(data), std::move(P));
}
void AdnlLocalId::deliver(AdnlNodeIdShort src, td::BufferSlice data) {
auto s = std::move(data);
for (auto &cb : cb_) {
auto f = cb.first;
if (f.length() <= s.length() && s.as_slice().substr(0, f.length()) == f) {
cb.second->receive_message(src, short_id_, std::move(s));
return;
}
}
VLOG(ADNL_INFO) << this << ": dropping IN message from " << src
<< ": no callbacks for custom message. firstint=" << td::TlParser(s.as_slice()).fetch_int();
}
void AdnlLocalId::deliver_query(AdnlNodeIdShort src, td::BufferSlice data, td::Promise<td::BufferSlice> promise) {
auto s = std::move(data);
for (auto &cb : cb_) {
auto f = cb.first;
if (f.length() <= s.length() && s.as_slice().substr(0, f.length()) == f) {
cb.second->receive_query(src, short_id_, std::move(s), std::move(promise));
return;
}
}
VLOG(ADNL_INFO) << this << ": dropping IN message from " << src
<< ": no callbacks for custom query. firstint=" << td::TlParser(s.as_slice()).fetch_int();
promise.set_error(td::Status::Error(ErrorCode::warning, "no callbacks for query"));
}
void AdnlLocalId::subscribe(std::string prefix, std::unique_ptr<AdnlPeerTable::Callback> callback) {
auto S = td::Slice(prefix);
for (auto &cb : cb_) {
auto G = td::Slice(cb.first);
if (S.size() < G.size()) {
LOG_CHECK(G.substr(0, S.size()) != S) << this << ": duplicate subscribe prefix";
} else {
LOG_CHECK(S.substr(0, G.size()) != G) << this << ": duplicate subscribe prefix";
}
}
cb_.emplace_back(prefix, std::move(callback));
}
void AdnlLocalId::unsubscribe(std::string prefix) {
bool deleted = false;
for (auto it = cb_.begin(); it != cb_.end();) {
if (it->first == prefix) {
it = cb_.erase(it);
deleted = true;
} else {
it++;
}
}
LOG_CHECK(deleted) << this << ": cannot unsubscribe: prefix not found";
}
void AdnlLocalId::update_address_list(AdnlAddressList addr_list) {
addr_list_ = std::move(addr_list);
addr_list_.set_reinit_date(Adnl::adnl_start_time());
addr_list_.set_version(static_cast<td::int32>(td::Clocks::system()));
VLOG(ADNL_INFO) << this << ": updated addr list. New version set to " << addr_list_.version();
publish_address_list();
}
void AdnlLocalId::publish_address_list() {
if (dht_node_.empty() || addr_list_.empty()) {
VLOG(ADNL_NOTICE) << this << ": skipping public addr list, because localid (or dht node) not fully initialized";
return;
}
dht::DhtKey dht_key{short_id_.pubkey_hash(), "address", 0};
auto dht_update_rule = dht::DhtUpdateRuleSignature::create().move_as_ok();
dht::DhtKeyDescription dht_key_description{std::move(dht_key), id_.pubkey(), std::move(dht_update_rule),
td::BufferSlice()};
auto B = serialize_tl_object(dht_key_description.tl(), true);
auto P = td::PromiseCreator::lambda([dht_node = dht_node_, SelfId = actor_id(this), addr_list = addr_list_.tl(),
dht_key_description = std::move(dht_key_description),
print_id = print_id()](td::Result<td::BufferSlice> R) mutable {
if (R.is_error()) {
LOG(ERROR) << print_id << ": cannot sign: " << R.move_as_error();
return;
}
dht_key_description.update_signature(R.move_as_ok());
dht_key_description.check().ensure();
auto ttl = static_cast<td::uint32>(td::Clocks::system() + 3600);
dht::DhtValue dht_value{std::move(dht_key_description), serialize_tl_object(addr_list, true), ttl,
td::BufferSlice("")};
auto B = serialize_tl_object(dht_value.tl(), true);
auto Q = td::PromiseCreator::lambda(
[dht_node, dht_value = std::move(dht_value), print_id](td::Result<td::BufferSlice> R) mutable {
if (R.is_error()) {
LOG(ERROR) << print_id << ": cannot sign: " << R.move_as_error();
return;
}
dht_value.update_signature(R.move_as_ok());
dht_value.check().ensure();
auto E = td::PromiseCreator::lambda([print_id](td::Result<td::Unit> R) {
if (R.is_error()) {
VLOG(ADNL_NOTICE) << print_id << ": failed to update addr list in DHT: " << R.move_as_error();
} else {
VLOG(ADNL_INFO) << print_id << ": updated dht addr list";
}
});
td::actor::send_closure(dht_node, &dht::Dht::set_value, std::move(dht_value), std::move(E));
});
td::actor::send_closure(SelfId, &AdnlLocalId::sign_async, std::move(B), std::move(Q));
});
td::actor::send_closure(keyring_, &keyring::Keyring::sign_message, short_id_.pubkey_hash(), std::move(B),
std::move(P));
}
AdnlLocalId::AdnlLocalId(AdnlNodeIdFull id, AdnlAddressList addr_list, td::actor::ActorId<AdnlPeerTable> peer_table,
td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<dht::Dht> dht_node) {
id_ = std::move(id);
short_id_ = id_.compute_short_id();
addr_list_ = std::move(addr_list);
if (addr_list_.addrs().size() > 0) {
addr_list_.set_version(static_cast<td::int32>(td::Clocks::system()));
}
peer_table_ = peer_table;
keyring_ = keyring;
dht_node_ = dht_node;
VLOG(ADNL_INFO) << this << ": created local id " << short_id_;
}
void AdnlLocalId::get_self_node(td::Promise<AdnlNode> promise) {
//addr_list_->version_ = static_cast<td::int32>(td::Clocks::system());
promise.set_value(AdnlNode{id_, addr_list_});
}
void AdnlLocalId::decrypt_message(td::BufferSlice data, td::Promise<td::BufferSlice> promise) {
td::actor::send_closure(keyring_, &keyring::Keyring::decrypt_message, short_id_.pubkey_hash(), std::move(data),
std::move(promise));
}
void AdnlLocalId::decrypt(td::BufferSlice data, td::Promise<AdnlPacket> promise) {
auto P = td::PromiseCreator::lambda(
[SelfId = actor_id(this), p = std::move(promise)](td::Result<td::BufferSlice> res) mutable {
if (res.is_error()) {
p.set_error(res.move_as_error());
} else {
td::actor::send_closure_later(SelfId, &AdnlLocalId::decrypt_continue, res.move_as_ok(), std::move(p));
}
});
td::actor::send_closure(keyring_, &keyring::Keyring::decrypt_message, short_id_.pubkey_hash(), std::move(data),
std::move(P));
}
void AdnlLocalId::decrypt_continue(td::BufferSlice data, td::Promise<AdnlPacket> promise) {
auto R = fetch_tl_object<ton_api::adnl_packetContents>(std::move(data), true);
if (R.is_error()) {
promise.set_error(R.move_as_error());
return;
}
auto packetR = AdnlPacket::create(R.move_as_ok());
if (packetR.is_error()) {
promise.set_error(packetR.move_as_error());
return;
}
promise.set_value(packetR.move_as_ok());
}
void AdnlLocalId::sign_async(td::BufferSlice data, td::Promise<td::BufferSlice> promise) {
td::actor::send_closure(keyring_, &keyring::Keyring::sign_message, short_id_.pubkey_hash(), std::move(data),
std::move(promise));
}
void AdnlLocalId::sign_batch_async(std::vector<td::BufferSlice> data,
td::Promise<std::vector<td::Result<td::BufferSlice>>> promise) {
td::actor::send_closure(keyring_, &keyring::Keyring::sign_messages, short_id_.pubkey_hash(), std::move(data),
std::move(promise));
}
void AdnlLocalId::start_up() {
publish_address_list();
alarm_timestamp() = td::Timestamp::in(AdnlPeerTable::republish_addr_list_timeout() * td::Random::fast(1.0, 2.0));
}
void AdnlLocalId::alarm() {
publish_address_list();
alarm_timestamp() = td::Timestamp::in(AdnlPeerTable::republish_addr_list_timeout() * td::Random::fast(1.0, 2.0));
}
void AdnlLocalId::update_packet(AdnlPacket packet, bool update_id, bool sign, td::int32 update_addr_list_if,
td::int32 update_priority_addr_list_if, td::Promise<AdnlPacket> promise) {
packet.init_random();
if (update_id) {
packet.set_source(id_);
}
if (!addr_list_.empty() && update_addr_list_if < addr_list_.version()) {
packet.set_addr_list(addr_list_);
}
if (!sign) {
promise.set_result(std::move(packet));
} else {
auto to_sign = packet.to_sign();
auto P = td::PromiseCreator::lambda(
[packet = std::move(packet), promise = std::move(promise)](td::Result<td::BufferSlice> R) mutable {
if (R.is_error()) {
promise.set_error(R.move_as_error());
} else {
packet.set_signature(R.move_as_ok());
promise.set_value(std::move(packet));
}
});
td::actor::send_closure(keyring_, &keyring::Keyring::sign_message, short_id_.pubkey_hash(), std::move(to_sign),
std::move(P));
}
}
} // namespace adnl
} // namespace ton

117
adnl/adnl-local-id.h Normal file
View file

@ -0,0 +1,117 @@
/*
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 <map>
#include "td/actor/actor.h"
#include "td/utils/BufferedUdp.h"
#include "auto/tl/ton_api.h"
#include "keys/encryptor.h"
#include "adnl-peer-table.h"
#include "dht/dht.h"
#include "adnl-peer-table.h"
#include "utils.hpp"
namespace ton {
namespace adnl {
class AdnlLocalId : public td::actor::Actor {
public:
AdnlNodeIdFull get_id() const;
AdnlNodeIdShort get_short_id() const;
AdnlAddressList get_addr_list() const;
void get_addr_list_async(td::Promise<AdnlAddressList> P) {
P.set_value(get_addr_list());
}
void update_dht_node(td::actor::ActorId<dht::Dht> dht_node) {
dht_node_ = dht_node;
publish_address_list();
}
void decrypt(td::BufferSlice data, td::Promise<AdnlPacket> promise);
void decrypt_continue(td::BufferSlice data, td::Promise<AdnlPacket> promise);
void decrypt_message(td::BufferSlice data, td::Promise<td::BufferSlice> promise);
void deliver(AdnlNodeIdShort src, td::BufferSlice data);
void deliver_query(AdnlNodeIdShort src, td::BufferSlice data, td::Promise<td::BufferSlice> promise);
void receive(td::BufferSlice data);
void subscribe(std::string prefix, std::unique_ptr<AdnlPeerTable::Callback> callback);
void unsubscribe(std::string prefix);
void update_address_list(AdnlAddressList addr_list);
void get_self_node(td::Promise<AdnlNode> promise);
void sign_async(td::BufferSlice data, td::Promise<td::BufferSlice> promise);
void sign_batch_async(std::vector<td::BufferSlice> data,
td::Promise<std::vector<td::Result<td::BufferSlice>>> promise);
AdnlLocalId(AdnlNodeIdFull id, AdnlAddressList addr_list, td::actor::ActorId<AdnlPeerTable> peer_table,
td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<dht::Dht> dht_node);
void start_up() override;
void alarm() override;
void update_packet(AdnlPacket packet, bool update_id, bool sign, td::int32 update_addr_list_if,
td::int32 update_priority_addr_list_if, td::Promise<AdnlPacket> promise);
struct PrintId {
AdnlNodeIdShort id;
};
PrintId print_id() const {
return PrintId{short_id_};
}
private:
td::actor::ActorId<AdnlPeerTable> peer_table_;
td::actor::ActorId<keyring::Keyring> keyring_;
td::actor::ActorId<dht::Dht> dht_node_;
std::vector<std::pair<std::string, std::unique_ptr<AdnlPeerTable::Callback>>> cb_;
AdnlAddressList addr_list_;
AdnlNodeIdFull id_;
AdnlNodeIdShort short_id_;
void publish_address_list();
};
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const AdnlLocalId::PrintId &id) {
sb << "[localid " << id.id << "]";
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const AdnlLocalId &localid) {
sb << localid.print_id();
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const AdnlLocalId *localid) {
sb << localid->print_id();
return sb;
}
} // namespace adnl
} // namespace ton

54
adnl/adnl-message.cpp Normal file
View file

@ -0,0 +1,54 @@
/*
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 "adnl/adnl-message.h"
#include "auto/tl/ton_api.hpp"
#include "td/utils/overloaded.h"
namespace ton {
namespace adnl {
AdnlMessage::AdnlMessage(tl_object_ptr<ton_api::adnl_Message> message) {
ton_api::downcast_call(
*message.get(),
td::overloaded(
[&](ton_api::adnl_message_createChannel &msg) {
message_ = adnlmessage::AdnlMessageCreateChannel{msg.key_, msg.date_};
},
[&](ton_api::adnl_message_confirmChannel &msg) {
message_ = adnlmessage::AdnlMessageConfirmChannel{msg.key_, msg.peer_key_, msg.date_};
},
[&](ton_api::adnl_message_custom &msg) { message_ = adnlmessage::AdnlMessageCustom{std::move(msg.data_)}; },
[&](ton_api::adnl_message_nop &msg) { message_ = adnlmessage::AdnlMessageNop{}; },
[&](ton_api::adnl_message_reinit &msg) { message_ = adnlmessage::AdnlMessageReinit{msg.date_}; },
[&](ton_api::adnl_message_query &msg) {
message_ = adnlmessage::AdnlMessageQuery{msg.query_id_, std::move(msg.query_)};
},
[&](ton_api::adnl_message_answer &msg) {
message_ = adnlmessage::AdnlMessageAnswer{msg.query_id_, std::move(msg.answer_)};
},
[&](ton_api::adnl_message_part &msg) {
message_ = adnlmessage::AdnlMessagePart{msg.hash_, static_cast<td::uint32>(msg.total_size_),
static_cast<td::uint32>(msg.offset_), std::move(msg.data_)};
}));
}
} // namespace adnl
} // namespace ton

296
adnl/adnl-message.h Normal file
View file

@ -0,0 +1,296 @@
/*
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 "adnl/adnl.h"
#include "adnl/adnl-query.h"
namespace ton {
namespace adnl {
namespace adnlmessage {
class AdnlMessageCreateChannel {
public:
AdnlMessageCreateChannel(pubkeys::Ed25519 key, td::int32 date) : key_(key), date_(date) {
}
const auto &key() const {
return key_;
}
auto date() const {
return date_;
}
td::uint32 size() const {
return 40;
}
tl_object_ptr<ton_api::adnl_Message> tl() const {
return create_tl_object<ton_api::adnl_message_createChannel>(key_.raw(), date_);
}
private:
pubkeys::Ed25519 key_;
td::int32 date_;
};
class AdnlMessageConfirmChannel {
public:
AdnlMessageConfirmChannel(pubkeys::Ed25519 key, pubkeys::Ed25519 peer_key, td::int32 date)
: key_(key), peer_key_(peer_key), date_(date) {
}
const auto &key() const {
return key_;
}
const auto &peer_key() const {
return peer_key_;
}
auto date() const {
return date_;
}
td::uint32 size() const {
return 72;
}
tl_object_ptr<ton_api::adnl_Message> tl() const {
return create_tl_object<ton_api::adnl_message_confirmChannel>(key_.raw(), peer_key_.raw(), date_);
}
private:
pubkeys::Ed25519 key_;
pubkeys::Ed25519 peer_key_;
td::int32 date_;
};
class AdnlMessageCustom {
public:
AdnlMessageCustom(td::BufferSlice data) : data_(std::move(data)) {
}
auto data() const {
return data_.clone();
}
td::uint32 size() const {
return static_cast<td::uint32>(data_.size()) + 12;
}
tl_object_ptr<ton_api::adnl_Message> tl() const {
return create_tl_object<ton_api::adnl_message_custom>(data_.clone());
}
private:
td::BufferSlice data_;
};
class AdnlMessageNop {
public:
AdnlMessageNop() {
}
td::uint32 size() const {
return 4;
}
tl_object_ptr<ton_api::adnl_Message> tl() const {
return create_tl_object<ton_api::adnl_message_nop>();
}
private:
};
class AdnlMessageReinit {
public:
AdnlMessageReinit(td::int32 date) : date_(date) {
}
auto date() const {
return date_;
}
td::uint32 size() const {
return 8;
}
tl_object_ptr<ton_api::adnl_Message> tl() const {
return create_tl_object<ton_api::adnl_message_reinit>(date_);
}
private:
td::int32 date_;
};
class AdnlMessageQuery {
public:
AdnlMessageQuery(AdnlQueryId query_id, td::BufferSlice data) : query_id_(query_id), data_(std::move(data)) {
}
const auto &query_id() const {
return query_id_;
}
auto data() const {
return data_.clone();
}
td::uint32 size() const {
return static_cast<td::uint32>(data_.size()) + 44;
}
tl_object_ptr<ton_api::adnl_Message> tl() const {
return create_tl_object<ton_api::adnl_message_query>(query_id_, data_.clone());
}
private:
AdnlQueryId query_id_;
td::BufferSlice data_;
};
class AdnlMessageAnswer {
public:
AdnlMessageAnswer(AdnlQueryId query_id, td::BufferSlice data) : query_id_(query_id), data_(std::move(data)) {
}
const auto &query_id() const {
return query_id_;
}
auto data() const {
return data_.clone();
}
td::uint32 size() const {
return static_cast<td::uint32>(data_.size()) + 44;
}
tl_object_ptr<ton_api::adnl_Message> tl() const {
return create_tl_object<ton_api::adnl_message_answer>(query_id_, data_.clone());
}
private:
AdnlQueryId query_id_;
td::BufferSlice data_;
};
class AdnlMessagePart {
public:
AdnlMessagePart(td::Bits256 hash, td::uint32 total_size, td::uint32 offset, td::BufferSlice data)
: hash_(hash), total_size_(total_size), offset_(offset), data_(std::move(data)) {
}
const auto &hash() const {
return hash_;
}
auto offset() const {
return offset_;
}
auto total_size() const {
return total_size_;
}
auto data() const {
return data_.clone();
}
td::uint32 size() const {
return static_cast<td::uint32>(data_.size()) + 48;
}
tl_object_ptr<ton_api::adnl_Message> tl() const {
return create_tl_object<ton_api::adnl_message_part>(hash_, total_size_, offset_, data_.clone());
}
private:
td::Bits256 hash_;
td::uint32 total_size_;
td::uint32 offset_;
td::BufferSlice data_;
};
} // namespace adnlmessage
class AdnlMessage {
public:
class Empty {
public:
Empty() {
}
td::uint32 size() const {
UNREACHABLE();
}
tl_object_ptr<ton_api::adnl_Message> tl() const {
UNREACHABLE();
}
};
private:
td::Variant<Empty, adnlmessage::AdnlMessageCreateChannel, adnlmessage::AdnlMessageConfirmChannel,
adnlmessage::AdnlMessageCustom, adnlmessage::AdnlMessageNop, adnlmessage::AdnlMessageReinit,
adnlmessage::AdnlMessageQuery, adnlmessage::AdnlMessageAnswer, adnlmessage::AdnlMessagePart>
message_{Empty{}};
public:
explicit AdnlMessage(tl_object_ptr<ton_api::adnl_Message> message);
template <class T>
AdnlMessage(T m) : message_(std::move(m)) {
}
tl_object_ptr<ton_api::adnl_Message> tl() const {
tl_object_ptr<ton_api::adnl_Message> res;
message_.visit([&](const auto &obj) { res = obj.tl(); });
return res;
}
td::uint32 size() const {
td::uint32 res;
message_.visit([&](const auto &obj) { res = obj.size(); });
return res;
}
template <class F>
void visit(F &&f) {
message_.visit(std::move(f));
}
template <class F>
void visit(F &&f) const {
message_.visit(std::move(f));
}
};
class AdnlMessageList {
public:
AdnlMessageList() {
}
AdnlMessageList(tl_object_ptr<ton_api::adnl_Message> message) {
auto msg = AdnlMessage{std::move(message)};
messages_.emplace_back(std::move(msg));
}
AdnlMessageList(std::vector<tl_object_ptr<ton_api::adnl_Message>> messages) {
for (auto &message : messages) {
messages_.push_back(AdnlMessage{std::move(message)});
}
}
void push_back(AdnlMessage message) {
messages_.push_back(std::move(message));
}
td::uint32 size() const {
return static_cast<td::uint32>(messages_.size());
}
tl_object_ptr<ton_api::adnl_Message> one_message() const {
CHECK(size() == 1);
return messages_[0].tl();
}
std::vector<tl_object_ptr<ton_api::adnl_Message>> mult_messages() const {
std::vector<tl_object_ptr<ton_api::adnl_Message>> vec;
for (auto &m : messages_) {
vec.emplace_back(m.tl());
}
return vec;
}
static std::vector<tl_object_ptr<ton_api::adnl_Message>> empty_vector() {
return std::vector<tl_object_ptr<ton_api::adnl_Message>>{};
}
auto &vector() {
return messages_;
}
private:
std::vector<AdnlMessage> messages_;
};
} // namespace adnl
} // namespace ton

View file

@ -0,0 +1,114 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "adnl-network-manager.hpp"
#include "adnl-peer-table.h"
namespace ton {
namespace adnl {
td::actor::ActorOwn<AdnlNetworkManager> AdnlNetworkManager::create(td::uint16 port) {
return td::actor::create_actor<AdnlNetworkManagerImpl>("NetworkManager", port);
}
void AdnlNetworkManagerImpl::add_listening_udp_port(td::uint16 port) {
class Callback : public td::UdpServer::Callback {
public:
Callback(td::actor::ActorShared<AdnlNetworkManagerImpl> manager) : manager_(std::move(manager)) {
}
private:
td::actor::ActorShared<AdnlNetworkManagerImpl> manager_;
void on_udp_message(td::UdpMessage udp_message) override {
td::actor::send_closure_later(manager_, &AdnlNetworkManagerImpl::receive_udp_message, std::move(udp_message));
}
};
auto X = td::UdpServer::create("udp server", port, std::make_unique<Callback>(actor_shared(this)));
X.ensure();
udp_servers_.emplace(port, X.move_as_ok());
}
void AdnlNetworkManagerImpl::receive_udp_message(td::UdpMessage message) {
if (!callback_) {
LOG(ERROR) << this << ": dropping IN message [?->?]: peer table unitialized";
return;
}
if (message.error.is_error()) {
VLOG(ADNL_WARNING) << this << ": dropping ERROR message: " << message.error;
return;
}
if (message.data.size() >= get_mtu()) {
VLOG(ADNL_NOTICE) << this << ": received huge packet of size " << message.data.size();
}
received_messages_++;
if (received_messages_ % 64 == 0) {
VLOG(ADNL_DEBUG) << this << ": received " << received_messages_ << "udp messages";
}
VLOG(ADNL_EXTRA_DEBUG) << this << ": received message of size " << message.data.size();
callback_->receive_packet(message.address, std::move(message.data));
}
void AdnlNetworkManagerImpl::send_udp_packet(AdnlNodeIdShort src_id, AdnlNodeIdShort dst_id, td::IPAddress dst_addr,
td::uint32 priority, td::BufferSlice data) {
auto randseed = 1; // use DST?
while (priority > 0) {
if (out_desc_[priority].size() > 0) {
break;
}
priority--;
}
if (out_desc_[priority].size() == 0) {
VLOG(ADNL_WARNING) << this << ": dropping OUT message [" << src_id << "->" << dst_id << "]: no out desc";
return;
}
auto &dv = out_desc_[priority];
auto &v = dv[randseed % dv.size()];
if (!v.is_proxy()) {
auto it = udp_servers_.find(static_cast<td::uint16>(v.addr.get_port()));
CHECK(it != udp_servers_.end());
td::UdpMessage M;
M.address = dst_addr;
M.data = std::move(data);
CHECK(M.data.size() <= get_mtu());
td::actor::send_closure(it->second, &td::UdpServer::send, std::move(M));
} else {
auto it = udp_servers_.find(out_udp_port_);
CHECK(it != udp_servers_.end());
auto enc = v.proxy->encrypt(
AdnlProxy::Packet{dst_addr.get_ipv4(), static_cast<td::uint16>(dst_addr.get_port()), std::move(data)});
td::UdpMessage M;
M.address = v.addr;
M.data = std::move(enc);
td::actor::send_closure(it->second, &td::UdpServer::send, std::move(M));
}
}
} // namespace adnl
} // namespace ton

107
adnl/adnl-network-manager.h Normal file
View file

@ -0,0 +1,107 @@
/*
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/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/port/IPAddress.h"
#include "adnl-node-id.hpp"
#include "adnl-proxy-types.h"
namespace td {
class UdpServer;
}
namespace ton {
namespace adnl {
class AdnlPeerTable;
class AdnlNetworkConnection : public td::actor::Actor {
public:
class Callback {
public:
virtual void on_change_state(bool ready) = 0;
virtual ~Callback() = default;
};
virtual void send(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::uint32 priority, td::BufferSlice message) = 0;
virtual bool is_alive() const = 0;
virtual bool is_active() const = 0;
virtual ~AdnlNetworkConnection() = default;
};
class AdnlNetworkManager : public td::actor::Actor {
public:
//using ConnHandle = td::uint64;
class Callback {
public:
virtual ~Callback() = default;
//virtual void receive_packet(td::IPAddress addr, ConnHandle conn_handle, td::BufferSlice data) = 0;
virtual void receive_packet(td::IPAddress addr, td::BufferSlice data) = 0;
};
static td::actor::ActorOwn<AdnlNetworkManager> create(td::uint16 out_port);
virtual ~AdnlNetworkManager() = default;
virtual void install_callback(std::unique_ptr<Callback> callback) = 0;
virtual void add_self_addr(td::IPAddress addr, td::uint32 priority) = 0;
virtual void add_proxy_addr(td::IPAddress addr, std::shared_ptr<AdnlProxy> proxy, td::uint32 priority) = 0;
virtual void send_udp_packet(AdnlNodeIdShort src_id, AdnlNodeIdShort dst_id, td::IPAddress dst_addr,
td::uint32 priority, td::BufferSlice data) = 0;
//virtual void send_tcp_packet(AdnlNodeIdShort src_id, AdnlNodeIdShort dst_id, td::IPAddress dst_addr,
// td::uint32 priority, td::BufferSlice data) = 0;
//virtual void send_answer_packet(AdnlNodeIdShort src_id, AdnlNodeIdShort dst_id, td::IPAddress dst_addr,
// ConnHandle conn_handle, td::uint32 priority, td::BufferSlice data) = 0;
static constexpr td::uint32 get_mtu() {
return 1440;
}
struct PrintId {};
PrintId print_id() const {
return PrintId{};
}
};
} // namespace adnl
} // namespace ton
namespace td {
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::adnl::AdnlNetworkManager::PrintId &id) {
sb << "[networkmanager]";
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::adnl::AdnlNetworkManager &manager) {
sb << manager.print_id();
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::adnl::AdnlNetworkManager *manager) {
sb << manager->print_id();
return sb;
}
} // namespace td

View file

@ -0,0 +1,121 @@
/*
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/actor/actor.h"
#include "td/utils/BufferedUdp.h"
#include "td/net/UdpServer.h"
#include "td/net/TcpListener.h"
#include "td/actor/PromiseFuture.h"
#include "adnl-network-manager.h"
#include <map>
namespace td {
class UdpServer;
}
namespace ton {
namespace adnl {
class AdnlPeerTable;
class AdnlNetworkManagerImpl : public AdnlNetworkManager {
public:
struct OutDesc {
td::IPAddress addr;
std::shared_ptr<AdnlProxy> proxy;
bool is_proxy() const {
return proxy != nullptr;
}
bool operator==(const OutDesc &with) const {
return addr == with.addr && is_proxy() == with.is_proxy();
}
};
AdnlNetworkManagerImpl(td::uint16 out_udp_port) : out_udp_port_(out_udp_port) {
}
void install_callback(std::unique_ptr<Callback> callback) override {
callback_ = std::move(callback);
}
void add_self_addr(td::IPAddress addr, td::uint32 priority) override {
auto x = OutDesc{addr, nullptr};
auto &v = out_desc_[priority];
for (auto &y : v) {
if (x == y) {
return;
}
}
out_desc_[priority].push_back(std::move(x));
add_listening_udp_port(static_cast<td::uint16>(addr.get_port()));
}
void add_proxy_addr(td::IPAddress addr, std::shared_ptr<AdnlProxy> proxy, td::uint32 priority) override {
auto x = OutDesc{addr, std::move(proxy)};
auto &v = out_desc_[priority];
for (auto &y : v) {
if (x == y) {
return;
}
}
out_desc_[priority].push_back(std::move(x));
if (!udp_servers_.count(out_udp_port_)) {
add_listening_udp_port(out_udp_port_);
}
}
void send_udp_packet(AdnlNodeIdShort src_id, AdnlNodeIdShort dst_id, td::IPAddress dst_addr, td::uint32 priority,
td::BufferSlice data) override;
void add_listening_udp_port(td::uint16 port);
void receive_udp_message(td::UdpMessage message);
private:
std::unique_ptr<Callback> callback_;
std::map<td::uint32, std::vector<OutDesc>> out_desc_;
td::uint64 received_messages_ = 0;
td::uint64 sent_messages_ = 0;
std::map<td::uint16, td::actor::ActorOwn<td::UdpServer>> udp_servers_;
td::uint16 out_udp_port_;
};
} // namespace adnl
} // namespace ton
namespace td {
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::adnl::AdnlNetworkManagerImpl &manager) {
sb << manager.print_id();
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::adnl::AdnlNetworkManagerImpl *manager) {
sb << manager->print_id();
return sb;
}
} // namespace td

130
adnl/adnl-node-id.hpp Normal file
View file

@ -0,0 +1,130 @@
/*
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 "keys/keys.hpp"
#include "common/io.hpp"
namespace ton {
namespace adnl {
class AdnlNodeIdShort {
public:
explicit AdnlNodeIdShort(const PublicKeyHash &hash) : hash_(hash) {
}
explicit AdnlNodeIdShort(PublicKeyHash &&hash) : hash_(std::move(hash)) {
}
AdnlNodeIdShort() {
}
explicit AdnlNodeIdShort(td::Slice data) : hash_(data) {
}
explicit AdnlNodeIdShort(td::Bits256 value) : hash_(value) {
}
explicit AdnlNodeIdShort(tl_object_ptr<ton_api::adnl_id_short> obj) : hash_(obj->id_) {
}
const auto &pubkey_hash() const {
return hash_;
}
bool operator==(const AdnlNodeIdShort &with) const {
return hash_ == with.hash_;
}
bool operator!=(const AdnlNodeIdShort &with) const {
return hash_ != with.hash_;
}
bool operator<(const AdnlNodeIdShort &with) const {
return hash_ < with.hash_;
}
tl_object_ptr<ton_api::adnl_id_short> tl() const {
return create_tl_object<ton_api::adnl_id_short>(hash_.tl());
}
auto as_slice() {
return hash_.as_slice();
}
auto as_slice() const {
return hash_.as_slice();
}
auto uint256_value() const {
return hash_.uint256_value();
}
auto bits256_value() const {
return hash_.bits256_value();
}
static AdnlNodeIdShort zero() {
return AdnlNodeIdShort{PublicKeyHash::zero()};
}
bool is_zero() const {
return hash_.is_zero();
}
private:
PublicKeyHash hash_;
};
class AdnlNodeIdFull {
private:
explicit AdnlNodeIdFull(const tl_object_ptr<ton_api::PublicKey> &pub) : pub_(pub) {
}
public:
explicit AdnlNodeIdFull(const PublicKey &pub) : pub_(pub) {
}
explicit AdnlNodeIdFull(PublicKey &&pub) : pub_(std::move(pub)) {
}
static td::Result<AdnlNodeIdFull> create(const tl_object_ptr<ton_api::PublicKey> &pub) {
return AdnlNodeIdFull{pub};
}
AdnlNodeIdFull() {
}
const auto &pubkey() const {
return pub_;
}
bool empty() const {
return pub_.empty();
}
bool operator==(const AdnlNodeIdFull &with) const {
return pub_ == with.pub_;
}
bool operator!=(const AdnlNodeIdFull &with) const {
return pub_ != with.pub_;
}
auto tl() const {
return pub_.tl();
}
AdnlNodeIdShort compute_short_id() const {
return AdnlNodeIdShort{pub_.compute_short_id()};
}
private:
PublicKey pub_;
};
} // namespace adnl
} // namespace ton
namespace td {
inline StringBuilder &operator<<(StringBuilder &stream, const ton::adnl::AdnlNodeIdShort &value) {
return stream << value.bits256_value();
}
} // namespace td

50
adnl/adnl-node.cpp Normal file
View file

@ -0,0 +1,50 @@
/*
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 "adnl-node.h"
namespace ton {
namespace adnl {
td::Result<AdnlNode> AdnlNode::create(const tl_object_ptr<ton_api::adnl_node> &obj) {
TRY_RESULT(id, AdnlNodeIdFull::create(obj->id_));
TRY_RESULT(addr_list, AdnlAddressList::create(std::move(obj->addr_list_)));
return AdnlNode{std::move(id), std::move(addr_list)};
}
tl_object_ptr<ton_api::adnl_nodes> AdnlNodesList::tl() const {
std::vector<tl_object_ptr<ton_api::adnl_node>> vec;
for (auto &node : nodes_) {
vec.emplace_back(node.tl());
}
return create_tl_object<ton_api::adnl_nodes>(std::move(vec));
}
td::Result<AdnlNodesList> AdnlNodesList::create(const tl_object_ptr<ton_api::adnl_nodes> &nodes) {
AdnlNodesList res{};
for (auto &node : nodes->nodes_) {
TRY_RESULT(N, AdnlNode::create(node));
res.push(std::move(N));
}
return res;
}
} // namespace adnl
} // namespace ton

74
adnl/adnl-node.h Normal file
View file

@ -0,0 +1,74 @@
/*
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 "auto/tl/ton_api.h"
#include "adnl-node-id.hpp"
#include "adnl-address-list.h"
namespace ton {
namespace adnl {
class AdnlNode {
private:
AdnlNodeIdFull pub_;
AdnlAddressList addr_list_;
public:
AdnlNode(AdnlNodeIdFull pub, AdnlAddressList addr_list) : pub_(std::move(pub)), addr_list_(std::move(addr_list)) {
}
AdnlNode(const AdnlNode& from) : pub_(from.pub_), addr_list_(from.addr_list_) {
}
static td::Result<AdnlNode> create(const tl_object_ptr<ton_api::adnl_node>& obj);
tl_object_ptr<ton_api::adnl_node> tl() const {
return create_tl_object<ton_api::adnl_node>(pub_.tl(), addr_list_.tl());
}
AdnlNodeIdFull pub_id() const {
return pub_;
}
AdnlNodeIdShort compute_short_id() const {
return pub_.compute_short_id();
}
const AdnlAddressList& addr_list() const {
return addr_list_;
}
};
class AdnlNodesList {
private:
std::vector<AdnlNode> nodes_;
public:
const auto& nodes() const {
return nodes_;
}
AdnlNodesList() {
}
void push(AdnlNode node) {
nodes_.push_back(std::move(node));
}
tl_object_ptr<ton_api::adnl_nodes> tl() const;
static td::Result<AdnlNodesList> create(const tl_object_ptr<ton_api::adnl_nodes>& nodes);
};
} // namespace adnl
} // namespace ton

135
adnl/adnl-packet.cpp Normal file
View file

@ -0,0 +1,135 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "adnl-packet.h"
#include "td/utils/Random.h"
namespace ton {
namespace adnl {
/*adnl.packetContents rand1:bytes flags:# from:flags.0?PublicKey from_short:flags.1?adnl.id.short
message:flags.2?adnl.Message messages:flags.3?(vector adnl.Message)
address:flags.6?adnl.addressList seqno:flags.8?long recv_addr_list_version:flags.9?int
confirm_seqno:flags.10?long reinit_date:flags.11?int dst_reinit_date:flags.11?int
signature:flags.7?bytes rand2:bytes = adnl.PacketContents;*/
td::Result<AdnlPacket> AdnlPacket::create(tl_object_ptr<ton_api::adnl_packetContents> packet) {
AdnlPacket R;
R.rand1_ = std::move(packet->rand1_);
R.flags_ = packet->flags_;
if (R.flags_ & Flags::f_from) {
TRY_RESULT(F, AdnlNodeIdFull::create(packet->from_));
R.from_ = std::move(F);
}
if (R.flags_ & Flags::f_from_short) {
R.from_short_ = AdnlNodeIdShort{packet->from_short_->id_};
} else if (packet->flags_ & Flags::f_from) {
R.from_short_ = R.from_.compute_short_id();
}
if (R.flags_ & Flags::f_one_message) {
R.messages_ = AdnlMessageList{std::move(packet->message_)};
}
if (R.flags_ & Flags::f_mult_messages) {
// may override messages_ if (flags & 0x4)
// but this message will fail in run_basic_checks()
// so it doesn't matter
R.messages_ = AdnlMessageList{std::move(packet->messages_)};
}
if (R.flags_ & Flags::f_address) {
TRY_RESULT(addr_list, AdnlAddressList::create(std::move(packet->address_)));
R.addr_ = std::move(addr_list);
}
if (R.flags_ & Flags::f_priority_address) {
TRY_RESULT(addr_list, AdnlAddressList::create(std::move(packet->address_)));
R.priority_addr_ = std::move(addr_list);
}
if (R.flags_ & Flags::f_seqno) {
R.seqno_ = packet->seqno_;
}
if (R.flags_ & Flags::f_confirm_seqno) {
R.confirm_seqno_ = packet->confirm_seqno_;
}
if (R.flags_ & Flags::f_recv_addr_version) {
R.recv_addr_list_version_ = packet->recv_addr_list_version_;
}
if (R.flags_ & Flags::f_recv_priority_addr_version) {
R.recv_priority_addr_list_version_ = packet->recv_priority_addr_list_version_;
}
if (R.flags_ & Flags::f_reinit_date) {
R.reinit_date_ = packet->reinit_date_;
R.dst_reinit_date_ = packet->dst_reinit_date_;
}
if (R.flags_ & Flags::f_signature) {
R.signature_ = std::move(packet->signature_);
}
R.rand2_ = std::move(packet->rand2_);
TRY_STATUS(R.run_basic_checks());
return std::move(R);
}
td::Status AdnlPacket::run_basic_checks() const {
if ((flags_ & Flags::f_all) != flags_) {
return td::Status::Error(ErrorCode::protoviolation, "bad flags");
}
if ((flags_ & Flags::f_one_message) && (flags_ & Flags::f_mult_messages)) {
return td::Status::Error(ErrorCode::protoviolation, "both flags 0x4 and 0x8 set");
}
if ((flags_ & Flags::f_from) && (flags_ & Flags::f_from_short) && from_.compute_short_id() != from_short_) {
return td::Status::Error(ErrorCode::protoviolation, "source and short source mismatch");
}
if ((flags_ & Flags::f_address) && addr_.empty()) {
return td::Status::Error(ErrorCode::protoviolation, "bad addr list");
}
if ((flags_ & Flags::f_priority_address) && priority_addr_.empty()) {
return td::Status::Error(ErrorCode::protoviolation, "bad addr list");
}
return td::Status::OK();
}
tl_object_ptr<ton_api::adnl_packetContents> AdnlPacket::tl() const {
return create_tl_object<ton_api::adnl_packetContents>(
rand1_.clone(), flags_ & ~Flags::f_priority, (flags_ & Flags::f_from) ? from_.tl() : nullptr,
(flags_ & Flags::f_from_short) ? from_short_.tl() : nullptr,
(flags_ & Flags::f_one_message) ? messages_.one_message() : nullptr,
(flags_ & Flags::f_mult_messages) ? messages_.mult_messages() : messages_.empty_vector(),
(flags_ & Flags::f_address) ? addr_.tl() : nullptr,
(flags_ & Flags::f_priority_address) ? priority_addr_.tl() : nullptr, seqno_, confirm_seqno_,
recv_addr_list_version_, recv_priority_addr_list_version_, reinit_date_, dst_reinit_date_, signature_.clone(),
rand2_.clone());
}
td::BufferSlice AdnlPacket::to_sign() const {
auto obj = tl();
obj->signature_.clear();
obj->flags_ &= ~Flags::f_signature;
CHECK(obj->signature_.size() == 0);
return serialize_tl_object(obj, true);
}
void AdnlPacket::init_random() {
rand1_ = td::BufferSlice{(td::Random::fast_uint32() & 1) ? 7u : 15u};
rand2_ = td::BufferSlice{(td::Random::fast_uint32() & 1) ? 7u : 15u};
td::Random::secure_bytes(rand1_.as_slice());
td::Random::secure_bytes(rand2_.as_slice());
}
} // namespace adnl
} // namespace ton

211
adnl/adnl-packet.h Normal file
View file

@ -0,0 +1,211 @@
/*
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 "adnl/adnl.h"
#include "adnl/adnl-message.h"
namespace ton {
namespace adnl {
/*
from:flags.0?PublicKey
from_short:flags.1?adnl.id.short
message:flags.2?adnl.Message
messages:flags.3?(vector adnl.Message)
address:flags.4?adnl.addressList
priority_address:flags.5?adnl.addressList
seqno:flags.6?long
confirm_seqno:flags.7?long
recv_addr_list_version:flags.8?int
recv_priority_addr_list_version:flags.9?int
reinit_date:flags.10?int
dst_reinit_date:flags.10?int
signature:flags.11?bytes
*/
// total packet length:
// for full packet:
// 32 (dst) + 64 (encryption overhead) + 4 (magic) + 36 (pubkey) + 4 + M (sum of messages) +
// + A1 + A2 + 8 + 8 + 4 + 4 + 4 + 4 + 68 (signature) + 16 (r1) + 16 (r2) =
// = 272 + M + A1 + A2
// for channel:
// 32 (channel id) + 32 (encryption overhead) + 4 (magic) + 4 + M (sum of messages) +
// + A1 + A2 + 8 + 8 + 4 + 4 + 16(r1) + 16(r2) = 128 + M + A1 + A2
class AdnlPacket {
private:
enum Flags : td::uint32 {
f_from = 0x1,
f_from_short = 0x2,
f_one_message = 0x4,
f_mult_messages = 0x8,
f_address = 0x10,
f_priority_address = 0x20,
f_seqno = 0x40,
f_confirm_seqno = 0x80,
f_recv_addr_version = 0x100,
f_recv_priority_addr_version = 0x200,
f_reinit_date = 0x400,
f_signature = 0x800,
f_priority = 0x1000,
f_all = 0x1fff
};
public:
AdnlPacket() {
}
static td::Result<AdnlPacket> create(tl_object_ptr<ton_api::adnl_packetContents> packet);
tl_object_ptr<ton_api::adnl_packetContents> tl() const;
td::BufferSlice to_sign() const;
td::Status run_basic_checks() const;
auto flags() const {
return flags_;
}
bool priority() const {
return flags_ & f_priority;
}
bool inited_from_short() const {
return flags_ & (Flags::f_from | Flags::f_from_short);
}
bool inited_from() const {
return flags_ & Flags::f_from;
}
auto from() const {
return from_;
}
auto from_short() const {
return from_short_;
}
const auto &messages() const {
return messages_;
}
auto &messages() {
return messages_;
}
bool inited_addr_list() const {
return flags_ & Flags::f_address;
}
auto addr_list() const {
return addr_;
}
auto priority_addr_list() const {
return priority_addr_;
}
auto seqno() const {
return seqno_;
}
auto confirm_seqno() const {
return confirm_seqno_;
}
auto recv_addr_list_version() const {
return recv_addr_list_version_;
}
auto recv_priority_addr_list_version() const {
return recv_priority_addr_list_version_;
}
auto reinit_date() const {
return reinit_date_;
}
auto dst_reinit_date() const {
return dst_reinit_date_;
}
auto signature() const {
return signature_.clone();
}
void init_random();
void set_signature(td::BufferSlice signature) {
signature_ = std::move(signature);
flags_ |= Flags::f_signature;
}
void set_source(AdnlNodeIdFull src) {
from_ = src;
from_short_ = src.compute_short_id();
flags_ = (flags_ | Flags::f_from) & ~Flags::f_from_short;
}
void set_source(AdnlNodeIdShort src) {
if (!(flags_ & Flags::f_from)) {
from_short_ = src;
flags_ |= Flags::f_from_short;
}
}
void add_message(AdnlMessage message) {
messages_.push_back(std::move(message));
if (messages_.size() == 1) {
flags_ = (flags_ | Flags::f_one_message) & ~Flags::f_mult_messages;
} else {
flags_ = (flags_ | Flags::f_mult_messages) & ~Flags::f_one_message;
}
}
void set_addr_list(AdnlAddressList addr_list) {
addr_ = std::move(addr_list);
flags_ |= Flags::f_address;
}
void set_priority_addr_list(AdnlAddressList addr_list) {
priority_addr_ = std::move(addr_list);
flags_ |= Flags::f_priority_address;
}
void set_seqno(td::uint64 seqno) {
seqno_ = seqno;
flags_ |= Flags::f_seqno;
}
void set_confirm_seqno(td::uint64 seqno) {
confirm_seqno_ = seqno;
flags_ |= Flags::f_confirm_seqno;
}
void set_received_addr_list_version(td::int32 version) {
recv_addr_list_version_ = version;
flags_ |= Flags::f_recv_addr_version;
}
void set_received_priority_addr_list_version(td::int32 version) {
recv_priority_addr_list_version_ = version;
flags_ |= Flags::f_recv_priority_addr_version;
}
void set_reinit_date(td::int32 date, td::int32 dst_reinit_date) {
reinit_date_ = date;
dst_reinit_date_ = dst_reinit_date;
flags_ |= Flags::f_reinit_date;
}
private:
td::BufferSlice rand1_;
td::uint32 flags_{0};
AdnlNodeIdFull from_;
AdnlNodeIdShort from_short_;
AdnlMessageList messages_;
AdnlAddressList addr_;
AdnlAddressList priority_addr_;
td::uint64 seqno_{0};
td::uint64 confirm_seqno_{0};
td::int32 recv_addr_list_version_{0};
td::int32 recv_priority_addr_list_version_{0};
td::int32 reinit_date_{0};
td::int32 dst_reinit_date_{0};
td::BufferSlice signature_;
td::BufferSlice rand2_;
};
} // namespace adnl
} // namespace ton

336
adnl/adnl-peer-table.cpp Normal file
View file

@ -0,0 +1,336 @@
/*
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 "adnl-peer-table.hpp"
#include "adnl-peer.h"
#include "adnl-channel.h"
#include "utils.hpp"
#include "td/utils/tl_storers.h"
#include "td/utils/crypto.h"
#include "td/utils/tl_parsers.h"
#include "td/utils/Random.h"
#include "td/db/RocksDb.h"
#include "utils.hpp"
#include "adnl-query.h"
#include "adnl-ext-client.h"
namespace ton {
namespace adnl {
td::int32 Adnl::adnl_start_time() {
static td::int32 start_time = [] {
auto init_start_time = static_cast<td::int32>(td::Clocks::system());
CHECK(init_start_time > 0);
return init_start_time;
}();
return start_time;
}
td::actor::ActorOwn<Adnl> Adnl::create(std::string db, td::actor::ActorId<keyring::Keyring> keyring) {
adnl_start_time();
return td::actor::ActorOwn<Adnl>(td::actor::create_actor<AdnlPeerTableImpl>("PeerTable", db, keyring));
}
void AdnlPeerTableImpl::receive_packet(td::BufferSlice data) {
if (data.size() < 32) {
VLOG(ADNL_WARNING) << this << ": dropping IN message [?->?]: message too short: len=" << data.size();
return;
}
AdnlNodeIdShort dst{data.as_slice().truncate(32)};
data.confirm_read(32);
auto it = local_ids_own_.find(dst);
if (it != local_ids_own_.end()) {
td::actor::send_closure(it->second, &AdnlLocalId::receive, std::move(data));
return;
}
AdnlChannelIdShort dst_chan_id{dst.pubkey_hash()};
auto it2 = channels_.find(dst_chan_id);
if (it2 != channels_.end()) {
td::actor::send_closure(it2->second, &AdnlChannel::receive, std::move(data));
return;
}
VLOG(ADNL_DEBUG) << this << ": dropping IN message [?->" << dst << "]: unknown dst " << dst
<< " (len=" << (data.size() + 32) << ")";
}
void AdnlPeerTableImpl::receive_decrypted_packet(AdnlNodeIdShort dst, AdnlPacket packet) {
packet.run_basic_checks().ensure();
if (!packet.inited_from_short()) {
VLOG(ADNL_INFO) << this << ": dropping IN message [?->" << dst << "]: destination not set";
return;
}
auto it = peers_.find(packet.from_short());
if (it == peers_.end()) {
if (!packet.inited_from()) {
VLOG(ADNL_NOTICE) << this << ": dropping IN message [" << packet.from_short() << "->" << dst
<< "]: unknown peer and no full src in packet";
return;
}
if (network_manager_.empty()) {
VLOG(ADNL_NOTICE) << this << ": dropping IN message [" << packet.from_short() << "->" << dst
<< "]: unknown peer and network manager uninitialized";
return;
}
it = peers_
.emplace(packet.from_short(),
AdnlPeer::create(network_manager_, actor_id(this), dht_node_, packet.from_short()))
.first;
CHECK(it != peers_.end());
}
auto it2 = local_ids_own_.find(dst);
if (it2 == local_ids_own_.end()) {
VLOG(ADNL_ERROR) << this << ": dropping IN message [" << packet.from_short() << "->" << dst
<< "]: unknown dst (but how did we decrypt message?)";
return;
}
td::actor::send_closure(it->second, &AdnlPeer::receive_packet, dst, it2->second.get(), std::move(packet));
}
void AdnlPeerTableImpl::add_peer(AdnlNodeIdShort local_id, AdnlNodeIdFull id, AdnlAddressList addr_list) {
auto id_short = id.compute_short_id();
VLOG(ADNL_DEBUG) << this << ": adding peer " << id_short << " for local id " << local_id;
auto it2 = local_ids_own_.find(local_id);
CHECK(it2 != local_ids_own_.end());
auto it = peers_.find(id_short);
if (it == peers_.end()) {
it = peers_.emplace(id_short, AdnlPeer::create(network_manager_, actor_id(this), dht_node_, id_short)).first;
CHECK(it != peers_.end());
}
td::actor::send_closure(it->second, &AdnlPeer::update_id, std::move(id));
if (!addr_list.empty()) {
td::actor::send_closure(it->second, &AdnlPeer::update_addr_list, local_id, it2->second.get(), std::move(addr_list));
}
}
void AdnlPeerTableImpl::add_static_nodes_from_config(AdnlNodesList nodes) {
for (auto &it : nodes.nodes()) {
add_static_node(it);
}
}
void AdnlPeerTableImpl::send_message_in(AdnlNodeIdShort src, AdnlNodeIdShort dst, AdnlMessage message) {
auto it = peers_.find(dst);
if (it == peers_.end()) {
it = peers_.emplace(dst, AdnlPeer::create(network_manager_, actor_id(this), dht_node_, dst)).first;
}
auto it2 = local_ids_own_.find(src);
if (it2 == local_ids_own_.end()) {
LOG(ERROR) << this << ": dropping OUT message [" << src << "->" << dst << "]: unknown src";
return;
}
td::actor::send_closure(it->second, &AdnlPeer::send_one_message, src, it2->second.get(), std::move(message));
}
void AdnlPeerTableImpl::answer_query(AdnlNodeIdShort src, AdnlNodeIdShort dst, AdnlQueryId query_id,
td::BufferSlice data) {
if (data.size() > get_mtu()) {
LOG(ERROR) << this << ": dropping OUT message [" << src << "->" << dst
<< "]: message too big: size=" << data.size();
return;
}
send_message_in(src, dst, adnlmessage::AdnlMessageAnswer{query_id, std::move(data)});
}
void AdnlPeerTableImpl::send_query(AdnlNodeIdShort src, AdnlNodeIdShort dst, std::string name,
td::Promise<td::BufferSlice> promise, td::Timestamp timeout, td::BufferSlice data) {
if (data.size() > huge_packet_max_size()) {
VLOG(ADNL_WARNING) << "dropping too big packet [" << src << "->" << dst << "]: size=" << data.size();
VLOG(ADNL_WARNING) << "DUMP: " << td::buffer_to_hex(data.as_slice().truncate(128));
return;
}
auto it = peers_.find(dst);
if (it == peers_.end()) {
it = peers_.emplace(dst, AdnlPeer::create(network_manager_, actor_id(this), dht_node_, dst)).first;
}
auto it2 = local_ids_own_.find(src);
if (it2 == local_ids_own_.end()) {
LOG(ERROR) << this << ": dropping OUT message [" << src << "->" << dst << "]: unknown src";
return;
}
td::actor::send_closure(it->second, &AdnlPeer::send_query, src, it2->second.get(), name, std::move(promise), timeout,
std::move(data));
}
void AdnlPeerTableImpl::add_id(AdnlNodeIdFull id, AdnlAddressList addr_list) {
auto a = id.compute_short_id();
VLOG(ADNL_INFO) << "adnl: adding local id " << a;
auto it = local_ids_own_.find(a);
if (it != local_ids_own_.end()) {
td::actor::send_closure(it->second, &AdnlLocalId::update_address_list, std::move(addr_list));
} else {
local_ids_own_[a] = td::actor::create_actor<AdnlLocalId>("localid", std::move(id), std::move(addr_list),
actor_id(this), keyring_, dht_node_);
}
}
void AdnlPeerTableImpl::del_id(AdnlNodeIdShort id, td::Promise<td::Unit> promise) {
VLOG(ADNL_INFO) << "adnl: deleting local id " << id;
local_ids_own_.erase(id);
promise.set_value(td::Unit());
}
void AdnlPeerTableImpl::subscribe(AdnlNodeIdShort dst, std::string prefix, std::unique_ptr<Callback> callback) {
auto it = local_ids_own_.find(dst);
LOG_CHECK(it != local_ids_own_.end()) << "dst=" << dst;
td::actor::send_closure(it->second, &AdnlLocalId::subscribe, prefix, std::move(callback));
}
void AdnlPeerTableImpl::unsubscribe(AdnlNodeIdShort dst, std::string prefix) {
auto it = local_ids_own_.find(dst);
if (it != local_ids_own_.end()) {
td::actor::send_closure(it->second, &AdnlLocalId::unsubscribe, prefix);
}
}
void AdnlPeerTableImpl::register_dht_node(td::actor::ActorId<dht::Dht> dht_node) {
dht_node_ = dht_node;
for (auto it = peers_.begin(); it != peers_.end(); it++) {
td::actor::send_closure(it->second, &AdnlPeer::update_dht_node, dht_node_);
}
for (auto it = local_ids_own_.begin(); it != local_ids_own_.end(); it++) {
td::actor::send_closure(it->second, &AdnlLocalId::update_dht_node, dht_node_);
}
}
void AdnlPeerTableImpl::register_network_manager(td::actor::ActorId<AdnlNetworkManager> network_manager) {
network_manager_ = std::move(network_manager);
class Cb : public AdnlNetworkManager::Callback {
public:
void receive_packet(td::IPAddress addr, td::BufferSlice data) override {
td::actor::send_closure(id_, &AdnlPeerTableImpl::receive_packet, std::move(data));
}
Cb(td::actor::ActorId<AdnlPeerTableImpl> id) : id_(id) {
}
private:
td::actor::ActorId<AdnlPeerTableImpl> id_;
};
auto cb = std::make_unique<Cb>(actor_id(this));
td::actor::send_closure(network_manager_, &AdnlNetworkManager::install_callback, std::move(cb));
}
void AdnlPeerTableImpl::get_addr_list(AdnlNodeIdShort id, td::Promise<AdnlAddressList> promise) {
auto it = local_ids_own_.find(id);
if (it == local_ids_own_.end()) {
promise.set_error(td::Status::Error(ErrorCode::notready));
return;
}
td::actor::send_closure(it->second, &AdnlLocalId::get_addr_list_async, std::move(promise));
}
void AdnlPeerTableImpl::get_self_node(AdnlNodeIdShort id, td::Promise<AdnlNode> promise) {
auto it = local_ids_own_.find(id);
if (it == local_ids_own_.end()) {
promise.set_error(td::Status::Error(ErrorCode::notready));
return;
}
td::actor::send_closure(it->second, &AdnlLocalId::get_self_node, std::move(promise));
}
void AdnlPeerTableImpl::register_channel(AdnlChannelIdShort id, td::actor::ActorId<AdnlChannel> channel) {
auto success = channels_.emplace(id, channel).second;
CHECK(success);
}
void AdnlPeerTableImpl::unregister_channel(AdnlChannelIdShort id) {
auto erased = channels_.erase(id);
CHECK(erased == 1);
}
void AdnlPeerTableImpl::start_up() {
}
void AdnlPeerTableImpl::write_new_addr_list_to_db(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id, AdnlDbItem node,
td::Promise<td::Unit> promise) {
td::actor::send_closure(db_, &AdnlDb::update, local_id, peer_id, std::move(node), std::move(promise));
}
void AdnlPeerTableImpl::get_addr_list_from_db(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id,
td::Promise<AdnlDbItem> promise) {
td::actor::send_closure(db_, &AdnlDb::get, local_id, peer_id, std::move(promise));
}
AdnlPeerTableImpl::AdnlPeerTableImpl(std::string db_root, td::actor::ActorId<keyring::Keyring> keyring) {
keyring_ = keyring;
static_nodes_manager_ = AdnlStaticNodesManager::create();
db_ = AdnlDb::create(db_root + "/adnl");
}
void AdnlPeerTableImpl::deliver(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data) {
auto it = local_ids_own_.find(dst);
if (it != local_ids_own_.end()) {
td::actor::send_closure(it->second, &AdnlLocalId::deliver, src, std::move(data));
}
}
void AdnlPeerTableImpl::deliver_query(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data,
td::Promise<td::BufferSlice> promise) {
auto it = local_ids_own_.find(dst);
if (it != local_ids_own_.end()) {
td::actor::send_closure(it->second, &AdnlLocalId::deliver_query, src, std::move(data), std::move(promise));
} else {
LOG(WARNING) << "deliver query: unknown dst " << dst;
promise.set_error(td::Status::Error(ErrorCode::notready, "cannot deliver: unknown DST"));
}
}
void AdnlPeerTableImpl::decrypt_message(AdnlNodeIdShort dst, td::BufferSlice data,
td::Promise<td::BufferSlice> promise) {
auto it = local_ids_own_.find(dst);
if (it != local_ids_own_.end()) {
td::actor::send_closure(it->second, &AdnlLocalId::decrypt_message, std::move(data), std::move(promise));
} else {
LOG(WARNING) << "decrypt message: unknown dst " << dst;
promise.set_error(td::Status::Error(ErrorCode::notready, "cannot decrypt: unknown DST"));
}
}
void AdnlPeerTableImpl::create_ext_server(std::vector<AdnlNodeIdShort> ids, std::vector<td::uint16> ports,
td::Promise<td::actor::ActorOwn<AdnlExtServer>> promise) {
promise.set_value(AdnlExtServerCreator::create(actor_id(this), std::move(ids), std::move(ports)));
}
} // namespace adnl
} // namespace ton

124
adnl/adnl-peer-table.h Normal file
View file

@ -0,0 +1,124 @@
/*
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/actor/actor.h"
#include "td/utils/BufferedUdp.h"
#include "adnl.h"
#include "utils.hpp"
#include "adnl/adnl-query.h"
#include "adnl/adnl-db.h"
#include "common/io.hpp"
#include "adnl-packet.h"
#include "auto/tl/ton_api.h"
namespace ton {
namespace adnl {
constexpr int VERBOSITY_NAME(ADNL_ERROR) = verbosity_WARNING;
constexpr int VERBOSITY_NAME(ADNL_WARNING) = verbosity_INFO;
constexpr int VERBOSITY_NAME(ADNL_NOTICE) = verbosity_DEBUG;
constexpr int VERBOSITY_NAME(ADNL_INFO) = verbosity_DEBUG;
constexpr int VERBOSITY_NAME(ADNL_DEBUG) = verbosity_DEBUG + 1;
constexpr int VERBOSITY_NAME(ADNL_EXTRA_DEBUG) = verbosity_DEBUG + 10;
class AdnlChannelIdShortImpl {
public:
explicit AdnlChannelIdShortImpl(PublicKeyHash value) {
value_ = value.bits256_value();
}
explicit AdnlChannelIdShortImpl(td::Bits256 value) {
value_ = value;
}
AdnlChannelIdShortImpl() {
}
td::Bits256 bits256_value() const {
return value_;
}
auto tl() const {
return value_;
}
bool operator<(const AdnlChannelIdShortImpl &with) const {
return value_ < with.value_;
}
bool operator==(const AdnlChannelIdShortImpl &with) const {
return value_ == with.value_;
}
bool operator!=(const AdnlChannelIdShortImpl &with) const {
return value_ != with.value_;
}
td::Slice as_slice() const {
return td::as_slice(value_);
}
private:
td::Bits256 value_;
};
using AdnlChannelIdShort = AdnlChannelIdShortImpl;
class AdnlLocalId;
class AdnlChannel;
class AdnlPeerTable : public Adnl {
public:
static constexpr double republish_addr_list_timeout() {
return 60.0;
}
virtual void answer_query(AdnlNodeIdShort src, AdnlNodeIdShort dst, AdnlQueryId query_id, td::BufferSlice data) = 0;
virtual void receive_packet(td::BufferSlice data) = 0;
virtual void receive_decrypted_packet(AdnlNodeIdShort dst, AdnlPacket packet) = 0;
virtual void send_message_in(AdnlNodeIdShort src, AdnlNodeIdShort dst, AdnlMessage message) = 0;
virtual void register_channel(AdnlChannelIdShort id, td::actor::ActorId<AdnlChannel> channel) = 0;
virtual void unregister_channel(AdnlChannelIdShort id) = 0;
virtual void add_static_node(AdnlNode node) = 0;
virtual void del_static_node(AdnlNodeIdShort id) = 0;
virtual void get_static_node(AdnlNodeIdShort id, td::Promise<AdnlNode> promise) = 0;
virtual void write_new_addr_list_to_db(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id, AdnlDbItem node,
td::Promise<td::Unit> promise) = 0;
virtual void get_addr_list_from_db(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id,
td::Promise<AdnlDbItem> promise) = 0;
virtual void deliver(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data) = 0;
virtual void deliver_query(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data,
td::Promise<td::BufferSlice> promise) = 0;
virtual void decrypt_message(AdnlNodeIdShort dst, td::BufferSlice data, td::Promise<td::BufferSlice> promise) = 0;
};
} // namespace adnl
} // namespace ton
namespace td {
inline td::StringBuilder &operator<<(td::StringBuilder &stream, const ton::adnl::AdnlChannelIdShort &value) {
return stream << value.bits256_value();
}
} // namespace td

137
adnl/adnl-peer-table.hpp Normal file
View file

@ -0,0 +1,137 @@
/*
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 <map>
#include <set>
#include "adnl-peer-table.h"
#include "adnl-peer.h"
#include "keys/encryptor.h"
//#include "adnl-decryptor.h"
#include "adnl-local-id.h"
#include "adnl-query.h"
#include "utils.hpp"
#include "adnl-static-nodes.h"
#include "adnl-ext-server.h"
#include "adnl-address-list.h"
namespace ton {
namespace adnl {
class AdnlPeerTableImpl : public AdnlPeerTable {
public:
AdnlPeerTableImpl(std::string db_root, td::actor::ActorId<keyring::Keyring> keyring);
void add_peer(AdnlNodeIdShort local_id, AdnlNodeIdFull id, AdnlAddressList addr_list) override;
void add_static_nodes_from_config(AdnlNodesList nodes) override;
void receive_packet(td::BufferSlice data) override;
void receive_decrypted_packet(AdnlNodeIdShort dst, AdnlPacket data) override;
void send_message_in(AdnlNodeIdShort src, AdnlNodeIdShort dst, AdnlMessage message) override;
void send_message(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data) override {
if (data.size() > huge_packet_max_size()) {
VLOG(ADNL_WARNING) << "dropping too big packet [" << src << "->" << dst << "]: size=" << data.size();
VLOG(ADNL_WARNING) << "DUMP: " << td::buffer_to_hex(data.as_slice().truncate(128));
return;
}
send_message_in(src, dst, AdnlMessage{adnlmessage::AdnlMessageCustom{std::move(data)}});
}
void answer_query(AdnlNodeIdShort src, AdnlNodeIdShort dst, AdnlQueryId query_id, td::BufferSlice data) override;
void send_query(AdnlNodeIdShort src, AdnlNodeIdShort dst, std::string name, td::Promise<td::BufferSlice> promise,
td::Timestamp timeout, td::BufferSlice data) override;
void send_query_ex(AdnlNodeIdShort src, AdnlNodeIdShort dst, std::string name, td::Promise<td::BufferSlice> promise,
td::Timestamp timeout, td::BufferSlice data, td::uint64 max_answer_size) override {
send_query(src, dst, name, std::move(promise), timeout, std::move(data));
}
void add_id(AdnlNodeIdFull id, AdnlAddressList addr_list) override;
void del_id(AdnlNodeIdShort id, td::Promise<td::Unit> promise) override;
void subscribe(AdnlNodeIdShort dst, std::string prefix, std::unique_ptr<Callback> callback) override;
void unsubscribe(AdnlNodeIdShort dst, std::string prefix) override;
void register_dht_node(td::actor::ActorId<dht::Dht> dht_node) override;
void register_network_manager(td::actor::ActorId<AdnlNetworkManager> network_manager) override;
void get_addr_list(AdnlNodeIdShort id, td::Promise<AdnlAddressList> promise) override;
void get_self_node(AdnlNodeIdShort id, td::Promise<AdnlNode> promise) override;
void start_up() override;
void register_channel(AdnlChannelIdShort id, td::actor::ActorId<AdnlChannel> channel) override;
void unregister_channel(AdnlChannelIdShort id) override;
void write_new_addr_list_to_db(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id, AdnlDbItem node,
td::Promise<td::Unit> promise) override;
void get_addr_list_from_db(AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id,
td::Promise<AdnlDbItem> promise) override;
void add_static_node(AdnlNode node) override {
CHECK(!static_nodes_manager_.empty());
td::actor::send_closure(static_nodes_manager_, &AdnlStaticNodesManager::add_node, std::move(node));
}
void del_static_node(AdnlNodeIdShort id) override {
td::actor::send_closure(static_nodes_manager_, &AdnlStaticNodesManager::del_node, id);
}
void get_static_node(AdnlNodeIdShort id, td::Promise<AdnlNode> promise) override {
td::actor::send_closure(static_nodes_manager_, &AdnlStaticNodesManager::get_node, id, std::move(promise));
}
void deliver(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data) override;
void deliver_query(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data,
td::Promise<td::BufferSlice> promise) override;
void decrypt_message(AdnlNodeIdShort dst, td::BufferSlice data, td::Promise<td::BufferSlice> promise) override;
void create_ext_server(std::vector<AdnlNodeIdShort> ids, std::vector<td::uint16> ports,
td::Promise<td::actor::ActorOwn<AdnlExtServer>> promise) override;
struct PrintId {};
PrintId print_id() const {
return PrintId{};
}
private:
td::actor::ActorId<keyring::Keyring> keyring_;
td::actor::ActorId<AdnlNetworkManager> network_manager_;
td::actor::ActorId<dht::Dht> dht_node_;
td::actor::ActorOwn<AdnlStaticNodesManager> static_nodes_manager_;
void deliver_one_message(AdnlNodeIdShort src, AdnlNodeIdShort dst, AdnlMessage message);
std::map<AdnlNodeIdShort, td::actor::ActorOwn<AdnlPeer>> peers_;
std::map<AdnlNodeIdShort, td::actor::ActorOwn<AdnlLocalId>> local_ids_own_;
std::map<AdnlChannelIdShort, td::actor::ActorId<AdnlChannel>> channels_;
td::actor::ActorOwn<AdnlDb> db_;
td::actor::ActorOwn<AdnlExtServer> ext_server_;
//std::map<td::uint64, td::actor::ActorId<AdnlQuery>> out_queries_;
//td::uint64 last_query_id_ = 1;
};
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const AdnlPeerTableImpl::PrintId &id) {
sb << "[peertable]";
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const AdnlPeerTableImpl &manager) {
sb << manager.print_id();
return sb;
}
} // namespace adnl
} // namespace ton

878
adnl/adnl-peer.cpp Normal file
View file

@ -0,0 +1,878 @@
/*
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 "adnl-peer.h"
#include "adnl-peer.hpp"
#include "adnl-local-id.h"
#include "utils.hpp"
#include "td/actor/PromiseFuture.h"
#include "td/utils/base64.h"
#include "td/utils/Random.h"
#include "auto/tl/ton_api.h"
namespace ton {
namespace adnl {
static_assert(AdnlPeerPairImpl::get_mtu() + AdnlPeerPairImpl::packet_header_max_size() <= AdnlNetworkManager::get_mtu(),
"wrong mtu configuration");
void AdnlPeerPairImpl::start_up() {
auto P1 = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<AdnlDbItem> R) {
td::actor::send_closure(SelfId, &AdnlPeerPairImpl::got_data_from_db, std::move(R));
});
td::actor::send_closure(peer_table_, &AdnlPeerTable::get_addr_list_from_db, local_id_, peer_id_short_, std::move(P1));
auto P2 = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<AdnlNode> R) {
td::actor::send_closure(SelfId, &AdnlPeerPairImpl::got_data_from_static_nodes, std::move(R));
});
td::actor::send_closure(peer_table_, &AdnlPeerTable::get_static_node, peer_id_short_, std::move(P2));
if (!dht_node_.empty()) {
discover();
}
}
void AdnlPeerPairImpl::alarm() {
if (next_dht_query_at_ && next_dht_query_at_.is_in_past()) {
next_dht_query_at_ = td::Timestamp::never();
discover();
}
if (next_db_update_at_ && next_db_update_at_.is_in_past()) {
if (received_from_db_ && received_from_static_nodes_ && !peer_id_.empty()) {
AdnlDbItem item;
item.id = peer_id_;
item.addr_list = addr_list_;
item.priority_addr_list = priority_addr_list_;
td::actor::send_closure(peer_table_, &AdnlPeerTable::write_new_addr_list_to_db, local_id_, peer_id_short_,
std::move(item), [](td::Unit) {});
}
next_db_update_at_ = td::Timestamp::in(td::Random::fast(60.0, 120.0));
}
if (retry_send_at_ && retry_send_at_.is_in_past()) {
retry_send_at_ = td::Timestamp::never();
send_messages_in(std::move(pending_messages_), false);
}
alarm_timestamp().relax(next_dht_query_at_);
alarm_timestamp().relax(next_db_update_at_);
alarm_timestamp().relax(retry_send_at_);
}
void AdnlPeerPairImpl::discover() {
CHECK(!dht_query_active_);
CHECK(!dht_node_.empty());
dht_query_active_ = true;
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id = print_id(),
peer_id = peer_id_short_](td::Result<dht::DhtValue> kv) {
if (kv.is_error()) {
td::actor::send_closure(SelfId, &AdnlPeerPairImpl::got_data_from_dht,
kv.move_as_error_prefix("failed to get from dht: "));
return;
}
auto k = kv.move_as_ok();
auto pub = AdnlNodeIdFull{k.key().public_key()};
CHECK(pub.compute_short_id() == peer_id);
auto addr_list = fetch_tl_object<ton_api::adnl_addressList>(k.value().clone(), true);
if (addr_list.is_error()) {
td::actor::send_closure(SelfId, &AdnlPeerPairImpl::got_data_from_dht,
addr_list.move_as_error_prefix("bad dht value: "));
return;
}
auto F = AdnlAddressList::create(addr_list.move_as_ok());
if (F.is_error()) {
td::actor::send_closure(SelfId, &AdnlPeerPairImpl::got_data_from_dht, F.move_as_error_prefix("bad dht value: "));
return;
}
AdnlNode node{pub, F.move_as_ok()};
td::actor::send_closure(SelfId, &AdnlPeerPairImpl::got_data_from_dht, std::move(node));
});
td::actor::send_closure(dht_node_, &dht::Dht::get_value, dht::DhtKey{peer_id_short_.pubkey_hash(), "address", 0},
std::move(P));
}
void AdnlPeerPairImpl::receive_packet_checked(AdnlPacket packet) {
auto d = Adnl::adnl_start_time();
if (packet.dst_reinit_date() > d) {
VLOG(ADNL_WARNING) << this << ": dropping IN message: too new our reinit date " << packet.dst_reinit_date();
return;
}
if (packet.reinit_date() > td::Clocks::system() + 60) {
VLOG(ADNL_NOTICE) << this << ": dropping IN message: too new peer reinit date " << packet.reinit_date();
return;
}
if (packet.reinit_date() > reinit_date_) {
reinit(packet.reinit_date());
}
if (packet.reinit_date() > 0 && packet.reinit_date() < reinit_date_) {
VLOG(ADNL_NOTICE) << this << ": dropping IN message: old peer reinit date " << packet.reinit_date();
return;
}
if (packet.dst_reinit_date() > 0 && packet.dst_reinit_date() < d) {
if (!packet.addr_list().empty()) {
update_addr_list(packet.addr_list());
}
if (!packet.priority_addr_list().empty()) {
update_addr_list(packet.priority_addr_list());
}
VLOG(ADNL_NOTICE) << this << ": dropping IN message old our reinit date " << packet.reinit_date() << " date=" << d;
auto M = AdnlMessage{adnlmessage::AdnlMessageNop{}};
send_message(std::move(M));
return;
}
if (packet.seqno() > 0) {
if (received_packet(static_cast<td::uint32>(packet.seqno()))) {
VLOG(ADNL_INFO) << this << ": dropping IN message: old seqno: " << packet.seqno() << " (current max " << in_seqno_
<< ")";
return;
}
}
if (packet.confirm_seqno() > 0) {
if (packet.confirm_seqno() > out_seqno_) {
VLOG(ADNL_WARNING) << this << ": dropping IN message: new ack seqno: " << packet.confirm_seqno()
<< " (current max sent " << out_seqno_ << ")";
return;
}
}
// accepted
// delivering
add_received_packet(static_cast<td::uint32>(packet.seqno()));
if (packet.confirm_seqno() > ack_seqno_) {
ack_seqno_ = packet.confirm_seqno();
}
if (packet.recv_addr_list_version() > peer_recv_addr_list_version_) {
peer_recv_addr_list_version_ = packet.recv_addr_list_version();
}
if (packet.recv_priority_addr_list_version() > peer_recv_priority_addr_list_version_) {
peer_recv_priority_addr_list_version_ = packet.recv_priority_addr_list_version();
}
if (!packet.addr_list().empty()) {
update_addr_list(packet.addr_list());
}
if (!packet.priority_addr_list().empty()) {
update_addr_list(packet.priority_addr_list());
}
received_messages_++;
if (received_messages_ % 64 == 0) {
VLOG(ADNL_INFO) << this << ": received " << received_messages_ << " messages";
}
for (auto &M : packet.messages().vector()) {
deliver_message(std::move(M));
}
}
void AdnlPeerPairImpl::receive_packet_from_channel(AdnlChannelIdShort id, AdnlPacket packet) {
if (id != channel_in_id_) {
VLOG(ADNL_NOTICE) << this << ": dropping IN message: outdated channel id" << id;
return;
}
channel_ready_ = true;
receive_packet_checked(std::move(packet));
}
void AdnlPeerPairImpl::receive_packet(AdnlPacket packet) {
packet.run_basic_checks().ensure();
if (!encryptor_) {
VLOG(ADNL_NOTICE) << this << "dropping IN message: unitialized id";
return;
}
auto S = encryptor_->check_signature(packet.to_sign().as_slice(), packet.signature().as_slice());
if (S.is_error()) {
VLOG(ADNL_NOTICE) << this << "dropping IN message: bad signature: " << S;
return;
}
receive_packet_checked(std::move(packet));
}
void AdnlPeerPairImpl::deliver_message(AdnlMessage message) {
message.visit([&](const auto &obj) { this->process_message(obj); });
}
void AdnlPeerPairImpl::send_messages_in(std::vector<AdnlMessage> messages, bool allow_postpone) {
auto connR = get_conn();
if (connR.is_error()) {
if (!allow_postpone) {
VLOG(ADNL_NOTICE) << this << ": dropping OUT messages: cannot get conn: " << connR.move_as_error();
return;
}
VLOG(ADNL_INFO) << this << ": delaying OUT messages: cannot get conn: " << connR.move_as_error();
if (!retry_send_at_) {
retry_send_at_.relax(td::Timestamp::in(10.0));
alarm_timestamp().relax(retry_send_at_);
}
for (auto &m : messages) {
pending_messages_.push_back(std::move(m));
}
return;
}
auto conn = connR.move_as_ok();
size_t ptr = 0;
bool first = true;
do {
size_t s = (channel_ready_ ? channel_packet_header_max_size() : packet_header_max_size());
if (first) {
s += 2 * addr_list_max_size();
}
AdnlPacket packet;
packet.set_seqno(++out_seqno_);
packet.set_confirm_seqno(in_seqno_);
if (first) {
if (!channel_inited_) {
auto M = adnlmessage::AdnlMessageCreateChannel{channel_pub_, channel_pk_date_};
s += M.size();
packet.add_message(std::move(M));
} else if (!channel_ready_) {
auto M = adnlmessage::AdnlMessageConfirmChannel{channel_pub_, peer_channel_pub_, channel_pk_date_};
s += M.size();
packet.add_message(std::move(M));
}
}
if (!addr_list_.empty()) {
packet.set_received_addr_list_version(addr_list_.version());
}
if (!priority_addr_list_.empty()) {
packet.set_received_priority_addr_list_version(priority_addr_list_.version());
}
while (ptr < messages.size()) {
auto &M = messages[ptr];
CHECK(M.size() <= get_mtu());
if (s + M.size() <= AdnlNetworkManager::get_mtu()) {
s += M.size();
packet.add_message(std::move(M));
ptr++;
} else {
break;
}
}
if (!channel_ready_) {
packet.set_reinit_date(Adnl::adnl_start_time(), reinit_date_);
packet.set_source(local_id_);
}
if (!first) {
if (!channel_inited_) {
auto M = adnlmessage::AdnlMessageCreateChannel{channel_pub_, channel_pk_date_};
if (s + M.size() <= AdnlNetworkManager::get_mtu()) {
s += M.size();
packet.add_message(std::move(M));
}
} else if (!channel_ready_) {
auto M = adnlmessage::AdnlMessageConfirmChannel{channel_pub_, peer_channel_pub_, channel_pk_date_};
if (s + M.size() <= AdnlNetworkManager::get_mtu()) {
s += M.size();
packet.add_message(std::move(M));
}
}
}
packet.run_basic_checks().ensure();
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), conn, id = print_id(),
via_channel = channel_ready_](td::Result<AdnlPacket> res) {
if (res.is_error()) {
LOG(ERROR) << id << ": dropping OUT message: error while creating packet: " << res.move_as_error();
} else {
td::actor::send_closure(SelfId, &AdnlPeerPairImpl::send_packet_continue, res.move_as_ok(), conn, via_channel);
}
});
td::actor::send_closure(
local_actor_, &AdnlLocalId::update_packet, std::move(packet),
!channel_ready_ && ack_seqno_ == 0 && in_seqno_ == 0, !channel_ready_,
(first || s + addr_list_max_size() <= AdnlNetworkManager::get_mtu()) ? peer_recv_addr_list_version_
: 0x7fffffff,
(first || s + 2 * addr_list_max_size() <= AdnlNetworkManager::get_mtu()) ? peer_recv_priority_addr_list_version_
: 0x7fffffff,
std::move(P));
first = false;
} while (ptr < messages.size());
}
void AdnlPeerPairImpl::send_messages(std::vector<AdnlMessage> messages) {
std::vector<AdnlMessage> new_vec;
for (auto &M : messages) {
if (M.size() <= get_mtu()) {
new_vec.push_back(std::move(M));
} else {
auto B = serialize_tl_object(M.tl(), true);
CHECK(B.size() <= huge_packet_max_size());
auto hash = sha256_bits256(B.as_slice());
auto size = static_cast<td::uint32>(B.size());
td::uint32 offset = 0;
td::uint32 part_size = Adnl::get_mtu();
while (offset < size) {
auto data = B.clone();
if (data.size() > part_size) {
data.truncate(part_size);
}
B.confirm_read(data.size());
new_vec.push_back(AdnlMessage{adnlmessage::AdnlMessagePart{hash, size, offset, std::move(data)}});
offset += part_size;
}
}
}
send_messages_in(std::move(new_vec), true);
}
void AdnlPeerPairImpl::send_packet_continue(AdnlPacket packet, td::actor::ActorId<AdnlNetworkConnection> conn,
bool via_channel) {
packet.run_basic_checks().ensure();
auto B = serialize_tl_object(packet.tl(), true);
if (via_channel) {
if (channel_ready_) {
td::actor::send_closure(channel_, &AdnlChannel::send_message, priority_, conn, std::move(B));
} else {
VLOG(ADNL_WARNING) << this << ": dropping OUT message [" << local_id_ << "->" << peer_id_short_
<< "]: channel destroyed in process";
}
return;
}
if (!encryptor_) {
VLOG(ADNL_INFO) << this << ": dropping OUT message [" << local_id_ << "->" << peer_id_short_
<< "]: empty encryptor";
return;
}
auto res = encryptor_->encrypt(B.as_slice());
if (res.is_error()) {
VLOG(ADNL_WARNING) << this << ": dropping OUT message [" << local_id_ << "->" << peer_id_short_
<< "]: failed to encrypt: " << res.move_as_error();
return;
}
auto X = res.move_as_ok();
auto enc = td::BufferSlice(X.size() + 32);
td::MutableSlice S = enc.as_slice();
S.copy_from(peer_id_short_.as_slice());
S.remove_prefix(32);
S.copy_from(X.as_slice());
td::actor::send_closure(conn, &AdnlNetworkConnection::send, local_id_, peer_id_short_, priority_, std::move(enc));
}
void AdnlPeerPairImpl::send_query(std::string name, td::Promise<td::BufferSlice> promise, td::Timestamp timeout,
td::BufferSlice data) {
AdnlQueryId id = AdnlQuery::random_query_id();
CHECK(out_queries_.count(id) == 0);
auto P = [SelfId = actor_id(this)](AdnlQueryId id) {
td::actor::send_closure(SelfId, &AdnlPeerPairImpl::delete_query, id);
};
out_queries_[id] = AdnlQuery::create(std::move(promise), std::move(P), name, timeout, id);
send_message(adnlmessage::AdnlMessageQuery{id, std::move(data)});
}
void AdnlPeerPairImpl::alarm_query(AdnlQueryId id) {
out_queries_.erase(id);
}
AdnlPeerPairImpl::AdnlPeerPairImpl(td::actor::ActorId<AdnlNetworkManager> network_manager,
td::actor::ActorId<AdnlPeerTable> peer_table,
td::actor::ActorId<AdnlLocalId> local_actor, td::actor::ActorId<AdnlPeer> peer,
td::actor::ActorId<dht::Dht> dht_node, AdnlNodeIdShort local_id,
AdnlNodeIdShort peer_id) {
network_manager_ = network_manager;
peer_table_ = peer_table;
local_actor_ = local_actor;
peer_ = peer;
dht_node_ = dht_node;
local_id_ = local_id;
peer_id_short_ = peer_id;
channel_pk_ = privkeys::Ed25519::random();
channel_pub_ = channel_pk_.pub();
channel_pk_date_ = static_cast<td::int32>(td::Clocks::system());
}
void AdnlPeerPairImpl::create_channel(pubkeys::Ed25519 pub, td::uint32 date) {
if (channel_inited_ && peer_channel_pub_ == pub) {
return;
}
if (channel_inited_ && date <= peer_channel_date_) {
return;
}
if (channel_inited_) {
td::actor::send_closure(peer_table_, &AdnlPeerTable::unregister_channel, channel_in_id_);
channel_.reset();
channel_inited_ = false;
channel_ready_ = false;
}
CHECK(!channel_ready_);
peer_channel_pub_ = pub;
peer_channel_date_ = date;
auto R = AdnlChannel::create(channel_pk_, peer_channel_pub_, local_id_, peer_id_short_, channel_out_id_,
channel_in_id_, actor_id(this));
if (R.is_ok()) {
channel_ = R.move_as_ok();
channel_inited_ = true;
td::actor::send_closure_later(peer_table_, &AdnlPeerTable::register_channel, channel_in_id_, channel_.get());
} else {
VLOG(ADNL_WARNING) << this << ": failed to create channel: " << R.move_as_error();
}
}
void AdnlPeerPairImpl::process_message(const adnlmessage::AdnlMessageCreateChannel &message) {
create_channel(message.key(), message.date());
}
void AdnlPeerPairImpl::process_message(const adnlmessage::AdnlMessageConfirmChannel &message) {
if (message.peer_key() != channel_pub_) {
VLOG(ADNL_NOTICE) << this << ": received adnl.message.confirmChannel with bad peer_key";
return;
}
create_channel(message.key(), message.date());
if (!channel_inited_ || peer_channel_pub_ != message.key()) {
VLOG(ADNL_NOTICE) << this << ": received adnl.message.confirmChannel with old key";
return;
}
channel_ready_ = true;
}
void AdnlPeerPairImpl::process_message(const adnlmessage::AdnlMessageCustom &message) {
td::actor::send_closure(local_actor_, &AdnlLocalId::deliver, peer_id_short_, message.data());
}
void AdnlPeerPairImpl::process_message(const adnlmessage::AdnlMessageNop &message) {
// nop
}
void AdnlPeerPairImpl::process_message(const adnlmessage::AdnlMessageReinit &message) {
reinit(message.date());
}
void AdnlPeerPairImpl::process_message(const adnlmessage::AdnlMessageQuery &message) {
auto P = td::PromiseCreator::lambda(
[SelfId = actor_id(this), query_id = message.query_id()](td::Result<td::BufferSlice> R) {
if (R.is_error()) {
LOG(WARNING) << "failed to answer query: " << R.move_as_error();
} else {
auto data = R.move_as_ok();
if (data.size() > Adnl::huge_packet_max_size()) {
LOG(WARNING) << "dropping too big answer query: size=" << data.size();
} else {
td::actor::send_closure(SelfId, &AdnlPeerPairImpl::send_message,
AdnlMessage{adnlmessage::AdnlMessageAnswer{query_id, std::move(data)}});
}
}
});
td::actor::send_closure(local_actor_, &AdnlLocalId::deliver_query, peer_id_short_, message.data(), std::move(P));
}
void AdnlPeerPairImpl::process_message(const adnlmessage::AdnlMessageAnswer &message) {
auto Q = out_queries_.find(message.query_id());
if (Q == out_queries_.end()) {
VLOG(ADNL_NOTICE) << this << ": dropping IN answer: unknown query id " << message.query_id();
return;
}
if (message.data().size() > Adnl::huge_packet_max_size()) {
VLOG(ADNL_NOTICE) << this << ": dropping IN answer: too big answer size";
return;
}
td::actor::send_closure_later(Q->second, &AdnlQuery::result, message.data());
out_queries_.erase(Q);
}
void AdnlPeerPairImpl::process_message(const adnlmessage::AdnlMessagePart &message) {
auto size = message.total_size();
if (size > huge_packet_max_size()) {
VLOG(ADNL_WARNING) << this << ": dropping too big huge message: size=" << size;
return;
}
if (message.hash().is_zero()) {
VLOG(ADNL_WARNING) << this << ": dropping huge message with zero hash";
return;
}
if (message.hash() != huge_message_hash_) {
huge_message_hash_.set_zero();
huge_message_.clear();
huge_message_offset_ = 0;
if (message.offset() == 0) {
huge_message_hash_ = message.hash();
huge_message_ = td::BufferSlice{size};
} else {
return;
}
}
auto data = message.data();
if (data.size() + message.offset() > size) {
VLOG(ADNL_WARNING) << this << ": dropping huge message with bad part";
return;
}
if (size != huge_message_.size()) {
VLOG(ADNL_WARNING) << this << ": dropping huge message part with inconsistent size";
return;
}
if (message.offset() == huge_message_offset_) {
auto S = huge_message_.as_slice();
S.remove_prefix(huge_message_offset_);
S.copy_from(data.as_slice());
huge_message_offset_ += static_cast<td::uint32>(data.size());
if (huge_message_offset_ == huge_message_.size()) {
//td::actor::send_closure(local_actor_, &AdnlLocalId::deliver, peer_id_short_, std::move(huge_message_));
if (sha256_bits256(huge_message_.as_slice()) != huge_message_hash_) {
VLOG(ADNL_WARNING) << this << ": dropping huge message: hash mismatch";
return;
}
huge_message_hash_.set_zero();
huge_message_offset_ = 0;
auto MR = fetch_tl_object<ton_api::adnl_Message>(std::move(huge_message_), true);
if (MR.is_error()) {
VLOG(ADNL_WARNING) << this << ": dropping huge message part with bad data";
return;
}
auto M = AdnlMessage{MR.move_as_ok()};
deliver_message(std::move(M));
}
}
}
void AdnlPeerPairImpl::delete_query(AdnlQueryId id) {
auto Q = out_queries_.find(id);
if (Q != out_queries_.end()) {
out_queries_.erase(Q);
}
}
void AdnlPeerPairImpl::reinit(td::int32 date) {
if (reinit_date_ == 0) {
reinit_date_ = date;
}
if (reinit_date_ < date) {
if (channel_inited_) {
td::actor::send_closure(peer_table_, &AdnlPeerTable::unregister_channel, channel_in_id_);
}
in_seqno_ = 0;
out_seqno_ = 0;
ack_seqno_ = 0;
recv_seqno_mask_ = 0;
channel_ready_ = false;
channel_inited_ = false;
peer_recv_addr_list_version_ = 0;
huge_message_offset_ = 0;
huge_message_hash_.set_zero();
huge_message_.clear();
channel_.release();
reinit_date_ = date;
}
}
td::Result<td::actor::ActorId<AdnlNetworkConnection>> AdnlPeerPairImpl::get_conn() {
if (!priority_addr_list_.empty() && priority_addr_list_.expire_at() < td::Clocks::system()) {
priority_addr_list_ = AdnlAddressList{};
priority_conns_.clear();
}
if (conns_.size() == 0 && priority_conns_.size() == 0) {
return td::Status::Error(ErrorCode::notready, PSTRING()
<< "empty network information: version=" << addr_list_.version()
<< " reinit_date=" << addr_list_.reinit_date()
<< " real_reinit_date=" << reinit_date_);
}
for (auto &conn : priority_conns_) {
if (conn.ready()) {
return conn.conn.get();
}
}
for (auto &conn : conns_) {
if (conn.ready()) {
return conn.conn.get();
}
}
return td::Status::Error(ErrorCode::notready, "no active connections");
}
void AdnlPeerPairImpl::update_addr_list(AdnlAddressList addr_list) {
if (addr_list.empty()) {
return;
}
CHECK(addr_list.size() > 0);
if (addr_list.reinit_date() > td::Clocks::system() + 60) {
VLOG(ADNL_WARNING) << "dropping addr list with too new reinit date";
return;
}
if (addr_list.reinit_date() > reinit_date_) {
reinit(addr_list.reinit_date());
} else if (addr_list.reinit_date() < reinit_date_) {
return;
}
bool priority = addr_list.priority() > 0;
if ((priority ? priority_addr_list_ : addr_list_).version() >= addr_list.version()) {
if (priority && priority_addr_list_.version() == addr_list.version()) {
auto expire_at = addr_list.expire_at();
if (expire_at > priority_addr_list_.expire_at()) {
priority_addr_list_.set_expire_at(expire_at);
}
}
return;
}
VLOG(ADNL_INFO) << this << ": updating addr list to version " << addr_list.version() << " size=" << addr_list.size();
const auto addrs = addr_list.addrs();
std::vector<Conn> conns;
conns.resize(std::min(addr_list.size(), 3u));
auto &old_conns = priority ? priority_conns_ : conns_;
for (size_t i = 0; i < conns.size(); i++) {
auto &addr = addrs[i];
auto hash = addr->get_hash();
if (i < old_conns.size() && old_conns[i].addr->get_hash() == hash) {
conns[i] = std::move(old_conns[i]);
} else {
conns[i] = Conn{addr, actor_id(this), network_manager_};
}
}
old_conns = std::move(conns);
(priority ? priority_addr_list_ : addr_list_) = addr_list;
}
void AdnlPeerImpl::update_id(AdnlNodeIdFull id) {
CHECK(id.compute_short_id() == peer_id_short_);
if (!peer_id_.empty()) {
return;
}
peer_id_ = std::move(id);
for (auto &it : peer_pairs_) {
td::actor::send_closure(it.second.get(), &AdnlPeerPair::update_peer_id, peer_id_);
}
}
void AdnlPeerPairImpl::Conn::create_conn(td::actor::ActorId<AdnlPeerPairImpl> peer,
td::actor::ActorId<AdnlNetworkManager> network_manager) {
auto id = addr->get_hash();
conn = addr->create_connection(network_manager, std::make_unique<ConnCallback>(peer, id));
}
void AdnlPeerPairImpl::conn_change_state(AdnlConnectionIdShort id, bool ready) {
if (ready) {
if (pending_messages_.size() > 0) {
send_messages_in(std::move(pending_messages_), true);
}
}
}
td::actor::ActorOwn<AdnlPeerPair> AdnlPeerPair::create(td::actor::ActorId<AdnlNetworkManager> network_manager,
td::actor::ActorId<AdnlPeerTable> peer_table,
td::actor::ActorId<AdnlLocalId> local_actor,
td::actor::ActorId<AdnlPeer> peer_actor,
td::actor::ActorId<dht::Dht> dht_node, AdnlNodeIdShort local_id,
AdnlNodeIdShort peer_id) {
auto X = td::actor::create_actor<AdnlPeerPairImpl>("peerpair", network_manager, peer_table, local_actor, peer_actor,
dht_node, local_id, peer_id);
return td::actor::ActorOwn<AdnlPeerPair>(std::move(X));
}
td::actor::ActorOwn<AdnlPeer> AdnlPeer::create(td::actor::ActorId<AdnlNetworkManager> network_manager,
td::actor::ActorId<AdnlPeerTable> peer_table,
td::actor::ActorId<dht::Dht> dht_node, AdnlNodeIdShort peer_id) {
auto X = td::actor::create_actor<AdnlPeerImpl>("peer", network_manager, peer_table, dht_node, peer_id);
return td::actor::ActorOwn<AdnlPeer>(std::move(X));
}
void AdnlPeerImpl::receive_packet(AdnlNodeIdShort dst, td::actor::ActorId<AdnlLocalId> dst_actor, AdnlPacket packet) {
if (packet.inited_from()) {
update_id(packet.from());
}
auto it = peer_pairs_.find(dst);
if (it == peer_pairs_.end()) {
auto X =
AdnlPeerPair::create(network_manager_, peer_table_, dst_actor, actor_id(this), dht_node_, dst, peer_id_short_);
peer_pairs_.emplace(dst, std::move(X));
it = peer_pairs_.find(dst);
CHECK(it != peer_pairs_.end());
if (!peer_id_.empty()) {
td::actor::send_closure(it->second.get(), &AdnlPeerPair::update_peer_id, peer_id_);
}
}
td::actor::send_closure(it->second.get(), &AdnlPeerPair::receive_packet_checked, std::move(packet));
}
void AdnlPeerImpl::send_messages(AdnlNodeIdShort src, td::actor::ActorId<AdnlLocalId> src_actor,
std::vector<AdnlMessage> messages) {
auto it = peer_pairs_.find(src);
if (it == peer_pairs_.end()) {
auto X =
AdnlPeerPair::create(network_manager_, peer_table_, src_actor, actor_id(this), dht_node_, src, peer_id_short_);
peer_pairs_.emplace(src, std::move(X));
it = peer_pairs_.find(src);
CHECK(it != peer_pairs_.end());
if (!peer_id_.empty()) {
td::actor::send_closure(it->second.get(), &AdnlPeerPair::update_peer_id, peer_id_);
}
}
td::actor::send_closure(it->second, &AdnlPeerPair::send_messages, std::move(messages));
}
void AdnlPeerImpl::send_query(AdnlNodeIdShort src, td::actor::ActorId<AdnlLocalId> src_actor, std::string name,
td::Promise<td::BufferSlice> promise, td::Timestamp timeout, td::BufferSlice data) {
auto it = peer_pairs_.find(src);
if (it == peer_pairs_.end()) {
auto X =
AdnlPeerPair::create(network_manager_, peer_table_, src_actor, actor_id(this), dht_node_, src, peer_id_short_);
peer_pairs_.emplace(src, std::move(X));
it = peer_pairs_.find(src);
CHECK(it != peer_pairs_.end());
if (!peer_id_.empty()) {
td::actor::send_closure(it->second.get(), &AdnlPeerPair::update_peer_id, peer_id_);
}
}
td::actor::send_closure(it->second, &AdnlPeerPair::send_query, name, std::move(promise), timeout, std::move(data));
}
void AdnlPeerImpl::del_local_id(AdnlNodeIdShort local_id) {
peer_pairs_.erase(local_id);
}
void AdnlPeerImpl::update_dht_node(td::actor::ActorId<dht::Dht> dht_node) {
dht_node_ = dht_node;
for (auto it = peer_pairs_.begin(); it != peer_pairs_.end(); it++) {
td::actor::send_closure(it->second, &AdnlPeerPair::update_dht_node, dht_node_);
}
}
void AdnlPeerImpl::update_addr_list(AdnlNodeIdShort local_id, td::actor::ActorId<AdnlLocalId> local_actor,
AdnlAddressList addr_list) {
auto it = peer_pairs_.find(local_id);
if (it == peer_pairs_.end()) {
auto X = AdnlPeerPair::create(network_manager_, peer_table_, local_actor, actor_id(this), dht_node_, local_id,
peer_id_short_);
peer_pairs_.emplace(local_id, std::move(X));
it = peer_pairs_.find(local_id);
CHECK(it != peer_pairs_.end());
if (!peer_id_.empty()) {
td::actor::send_closure(it->second.get(), &AdnlPeerPair::update_peer_id, peer_id_);
}
}
td::actor::send_closure(it->second, &AdnlPeerPair::update_addr_list, std::move(addr_list));
}
void AdnlPeerPairImpl::got_data_from_db(td::Result<AdnlDbItem> R) {
received_from_db_ = false;
if (R.is_error()) {
return;
}
auto value = R.move_as_ok();
if (!value.id.empty()) {
update_peer_id(value.id);
}
update_addr_list(value.addr_list);
update_addr_list(value.priority_addr_list);
}
void AdnlPeerPairImpl::got_data_from_static_nodes(td::Result<AdnlNode> R) {
received_from_static_nodes_ = false;
if (R.is_error()) {
return;
}
auto value = R.move_as_ok();
if (!value.pub_id().empty()) {
update_peer_id(value.pub_id());
}
update_addr_list(value.addr_list());
}
void AdnlPeerPairImpl::got_data_from_dht(td::Result<AdnlNode> R) {
CHECK(dht_query_active_);
dht_query_active_ = false;
next_dht_query_at_ = td::Timestamp::in(td::Random::fast(60.0, 120.0));
if (R.is_error()) {
VLOG(ADNL_INFO) << this << ": dht query failed: " << R.move_as_error();
return;
}
auto value = R.move_as_ok();
if (!value.pub_id().empty()) {
update_peer_id(value.pub_id());
}
update_addr_list(value.addr_list());
}
void AdnlPeerPairImpl::update_peer_id(AdnlNodeIdFull id) {
if (peer_id_.empty()) {
peer_id_ = std::move(id);
auto R = peer_id_.pubkey().create_encryptor();
if (R.is_ok()) {
encryptor_ = R.move_as_ok();
} else {
VLOG(ADNL_WARNING) << this << ": failed to create encryptor: " << R.move_as_error();
}
}
CHECK(!peer_id_.empty());
}
} // namespace adnl
} // namespace ton

101
adnl/adnl-peer.h Normal file
View file

@ -0,0 +1,101 @@
/*
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/actor/actor.h"
#include "td/utils/BufferedUdp.h"
#include "dht/dht.h"
#include "adnl-peer-table.h"
#include "utils.hpp"
#include "auto/tl/ton_api.h"
namespace ton {
namespace adnl {
class AdnlPeerTable;
class AdnlNetworkManager;
class AdnlLocalId;
class AdnlNetworkConnection;
class AdnlPeer;
class AdnlPeerPair : public td::actor::Actor {
public:
virtual void receive_packet_from_channel(AdnlChannelIdShort id, AdnlPacket packet) = 0;
virtual void receive_packet_checked(AdnlPacket packet) = 0;
virtual void receive_packet(AdnlPacket packet) = 0;
virtual void send_messages(std::vector<AdnlMessage> message) = 0;
inline void send_message(AdnlMessage message) {
std::vector<AdnlMessage> vec;
vec.push_back(std::move(message));
send_messages(std::move(vec));
}
static constexpr td::uint32 get_mtu() {
return Adnl::get_mtu() + 128;
}
virtual void send_query(std::string name, td::Promise<td::BufferSlice> promise, td::Timestamp timeout,
td::BufferSlice data) = 0;
virtual void alarm_query(AdnlQueryId query_id) = 0;
virtual void update_dht_node(td::actor::ActorId<dht::Dht> dht_node) = 0;
virtual void update_peer_id(AdnlNodeIdFull id) = 0;
virtual void update_addr_list(AdnlAddressList addr_list) = 0;
static td::actor::ActorOwn<AdnlPeerPair> create(td::actor::ActorId<AdnlNetworkManager> network_manager,
td::actor::ActorId<AdnlPeerTable> peer_table,
td::actor::ActorId<AdnlLocalId> local_actor,
td::actor::ActorId<AdnlPeer> peer_actor,
td::actor::ActorId<dht::Dht> dht_node, AdnlNodeIdShort local_id,
AdnlNodeIdShort peer_id);
};
class AdnlPeer : public td::actor::Actor {
public:
virtual void receive_packet(AdnlNodeIdShort dst, td::actor::ActorId<AdnlLocalId> dst_actor, AdnlPacket message) = 0;
virtual void send_messages(AdnlNodeIdShort src, td::actor::ActorId<AdnlLocalId> src_actor,
std::vector<AdnlMessage> messages) = 0;
virtual void send_query(AdnlNodeIdShort src, td::actor::ActorId<AdnlLocalId> src_actor, std::string name,
td::Promise<td::BufferSlice> promise, td::Timestamp timeout, td::BufferSlice data) = 0;
void send_one_message(AdnlNodeIdShort src, td::actor::ActorId<AdnlLocalId> src_actor, AdnlMessage message) {
std::vector<AdnlMessage> vec;
vec.push_back(std::move(message));
send_messages(src, src_actor, std::move(vec));
}
void send_message(AdnlNodeIdShort src, td::actor::ActorId<AdnlLocalId> src_actor, td::BufferSlice data) {
auto M = AdnlMessage{adnlmessage::AdnlMessageCustom{std::move(data)}};
send_one_message(src, src_actor, std::move(M));
}
static td::actor::ActorOwn<AdnlPeer> create(td::actor::ActorId<AdnlNetworkManager> network_manager,
td::actor::ActorId<AdnlPeerTable> peer_table,
td::actor::ActorId<dht::Dht> dht_node, AdnlNodeIdShort peer_id);
virtual void del_local_id(AdnlNodeIdShort local_id) = 0;
virtual void update_id(AdnlNodeIdFull id) = 0;
virtual void update_addr_list(AdnlNodeIdShort local_id, td::actor::ActorId<AdnlLocalId> local_actor,
AdnlAddressList addr_list) = 0;
virtual void update_dht_node(td::actor::ActorId<dht::Dht> dht_node) = 0;
};
} // namespace adnl
} // namespace ton

318
adnl/adnl-peer.hpp Normal file
View file

@ -0,0 +1,318 @@
/*
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 <map>
#include "adnl-peer.h"
#include "adnl-peer-table.h"
#include "adnl-network-manager.h"
#include "keys/encryptor.h"
#include "adnl-channel.h"
#include "adnl-query.h"
#include "crypto/Ed25519.h"
#include "td/utils/DecTree.h"
#include "utils.hpp"
namespace ton {
namespace adnl {
using AdnlConnectionIdShort = AdnlAddressImpl::Hash;
class AdnlPeerPairImpl : public AdnlPeerPair {
public:
static constexpr td::uint32 packet_header_max_size() {
return 272;
}
static constexpr td::uint32 channel_packet_header_max_size() {
return 128;
}
static constexpr td::uint32 addr_list_max_size() {
return 128;
}
static constexpr td::uint32 get_mtu() {
return Adnl::get_mtu() + 128;
}
static constexpr td::uint32 huge_packet_max_size() {
return Adnl::huge_packet_max_size() + 128;
}
AdnlPeerPairImpl(td::actor::ActorId<AdnlNetworkManager> network_manager, td::actor::ActorId<AdnlPeerTable> peer_table,
td::actor::ActorId<AdnlLocalId> local_actor, td::actor::ActorId<AdnlPeer> peer,
td::actor::ActorId<dht::Dht> dht_node, AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id);
void start_up() override;
void alarm() override;
void discover();
void receive_packet_from_channel(AdnlChannelIdShort id, AdnlPacket packet) override;
void receive_packet_checked(AdnlPacket packet) override;
void receive_packet(AdnlPacket packet) override;
void deliver_message(AdnlMessage message);
void send_messages_in(std::vector<AdnlMessage> messages, bool allow_postpone);
void send_messages(std::vector<AdnlMessage> messages) override;
void send_packet_continue(AdnlPacket packet, td::actor::ActorId<AdnlNetworkConnection> conn, bool via_channel);
void send_query(std::string name, td::Promise<td::BufferSlice> promise, td::Timestamp timeout,
td::BufferSlice data) override;
void alarm_query(AdnlQueryId id) override;
void discover_query_result(td::Result<dht::DhtValue> B, bool dummy);
void update_dht_node(td::actor::ActorId<dht::Dht> dht_node) override {
dht_node_ = dht_node;
}
void update_addr_list(AdnlAddressList addr_list) override;
void update_peer_id(AdnlNodeIdFull id) override;
void got_data_from_db(td::Result<AdnlDbItem> R);
void got_data_from_static_nodes(td::Result<AdnlNode> R);
void got_data_from_dht(td::Result<AdnlNode> R);
//void conn_ready(AdnlConnectionIdShort id, td::Result<td::actor::ActorOwn<AdnlNetworkConnection>> R);
void process_message(const adnlmessage::AdnlMessageCreateChannel &message);
void process_message(const adnlmessage::AdnlMessageConfirmChannel &message);
void process_message(const adnlmessage::AdnlMessageCustom &message);
void process_message(const adnlmessage::AdnlMessageNop &message);
void process_message(const adnlmessage::AdnlMessageReinit &message);
void process_message(const adnlmessage::AdnlMessageQuery &message);
void process_message(const adnlmessage::AdnlMessageAnswer &message);
void process_message(const adnlmessage::AdnlMessagePart &message);
void process_message(const AdnlMessage::Empty &message) {
UNREACHABLE();
}
void conn_change_state(AdnlConnectionIdShort conn_id, bool ready);
void delete_query(AdnlQueryId id);
struct PrintId {
AdnlNodeIdShort peer_id;
AdnlNodeIdShort local_id;
};
PrintId print_id() const {
return PrintId{peer_id_short_, local_id_};
}
private:
void reinit(td::int32 date);
td::Result<td::actor::ActorId<AdnlNetworkConnection>> get_conn();
void create_channel(pubkeys::Ed25519 pub, td::uint32 date);
bool received_packet(td::uint32 seqno) const {
CHECK(seqno > 0);
if (seqno + 64 <= in_seqno_) {
return true;
}
if (seqno > in_seqno_) {
return false;
}
return recv_seqno_mask_ & (1ull << (in_seqno_ - seqno));
}
void add_received_packet(td::uint32 seqno) {
CHECK(!received_packet(seqno));
if (seqno <= in_seqno_) {
recv_seqno_mask_ |= (1ull << (in_seqno_ - seqno));
} else {
auto old = in_seqno_;
in_seqno_ = seqno;
if (in_seqno_ - old >= 64) {
recv_seqno_mask_ = 1;
} else {
recv_seqno_mask_ = recv_seqno_mask_ << (in_seqno_ - old);
recv_seqno_mask_ |= 1;
}
}
}
struct Conn {
class ConnCallback : public AdnlNetworkConnection::Callback {
public:
void on_change_state(bool ready) override {
td::actor::send_closure(root_, &AdnlPeerPairImpl::conn_change_state, conn_id_, ready);
}
ConnCallback(td::actor::ActorId<AdnlPeerPairImpl> root, AdnlConnectionIdShort conn_id)
: root_(root), conn_id_(conn_id) {
}
private:
td::actor::ActorId<AdnlPeerPairImpl> root_;
AdnlConnectionIdShort conn_id_;
};
AdnlAddress addr;
td::actor::ActorOwn<AdnlNetworkConnection> conn;
Conn(AdnlAddress addr, td::actor::ActorId<AdnlPeerPairImpl> peer,
td::actor::ActorId<AdnlNetworkManager> network_manager)
: addr(std::move(addr)) {
create_conn(peer, network_manager);
}
Conn() {
}
bool ready() {
return !conn.empty() && conn.get_actor_unsafe().is_active();
}
void create_conn(td::actor::ActorId<AdnlPeerPairImpl> peer, td::actor::ActorId<AdnlNetworkManager> network_manager);
};
std::vector<AdnlMessage> pending_messages_;
td::actor::ActorId<AdnlNetworkManager> network_manager_;
td::actor::ActorId<AdnlPeerTable> peer_table_;
td::actor::ActorId<AdnlLocalId> local_actor_;
td::actor::ActorId<AdnlPeer> peer_;
td::actor::ActorId<dht::Dht> dht_node_;
td::uint32 priority_ = 0;
td::int32 reinit_date_ = 0;
bool channel_ready_ = false;
bool channel_inited_ = false;
AdnlChannelIdShort channel_in_id_;
AdnlChannelIdShort channel_out_id_;
privkeys::Ed25519 channel_pk_;
pubkeys::Ed25519 channel_pub_;
td::int32 channel_pk_date_;
td::actor::ActorOwn<AdnlChannel> channel_;
td::uint64 in_seqno_ = 0;
td::uint64 out_seqno_ = 0;
td::uint64 ack_seqno_ = 0;
td::uint64 recv_seqno_mask_ = 0;
td::uint32 peer_channel_date_ = 0;
pubkeys::Ed25519 peer_channel_pub_;
td::int32 peer_recv_addr_list_version_ = -1;
td::int32 peer_recv_priority_addr_list_version_ = -1;
td::Bits256 huge_message_hash_ = td::Bits256::zero();
td::BufferSlice huge_message_;
td::uint32 huge_message_offset_ = 0;
AdnlAddressList addr_list_;
AdnlAddressList priority_addr_list_;
std::vector<Conn> conns_;
std::vector<Conn> priority_conns_;
AdnlNodeIdFull peer_id_;
AdnlNodeIdShort peer_id_short_;
AdnlNodeIdShort local_id_;
std::unique_ptr<Encryptor> encryptor_;
std::map<AdnlQueryId, td::actor::ActorId<AdnlQuery>> out_queries_;
td::uint32 received_messages_ = 0;
bool received_from_db_ = false;
bool received_from_static_nodes_ = false;
bool dht_query_active_ = false;
td::Timestamp next_dht_query_at_ = td::Timestamp::never();
td::Timestamp next_db_update_at_ = td::Timestamp::never();
td::Timestamp retry_send_at_ = td::Timestamp::never();
};
class AdnlPeerImpl : public AdnlPeer {
public:
void receive_packet(AdnlNodeIdShort dst, td::actor::ActorId<AdnlLocalId> dst_actor, AdnlPacket packet) override;
void send_messages(AdnlNodeIdShort src, td::actor::ActorId<AdnlLocalId> src_actor,
std::vector<AdnlMessage> messages) override;
void send_query(AdnlNodeIdShort src, td::actor::ActorId<AdnlLocalId> src_actor, std::string name,
td::Promise<td::BufferSlice> promise, td::Timestamp timeout, td::BufferSlice data) override;
void del_local_id(AdnlNodeIdShort local_id) override;
void update_id(AdnlNodeIdFull id) override;
void update_addr_list(AdnlNodeIdShort local_id, td::actor::ActorId<AdnlLocalId> local_actor,
AdnlAddressList addr_list) override;
void update_dht_node(td::actor::ActorId<dht::Dht> dht_node) override;
//void check_signature(td::BufferSlice data, td::BufferSlice signature, td::Promise<td::Unit> promise) override;
AdnlPeerImpl(td::actor::ActorId<AdnlNetworkManager> network_manager, td::actor::ActorId<AdnlPeerTable> peer_table,
td::actor::ActorId<dht::Dht> dht_node, AdnlNodeIdShort peer_id)
: peer_id_short_(peer_id), dht_node_(dht_node), peer_table_(peer_table), network_manager_(network_manager) {
}
struct PrintId {
AdnlNodeIdShort peer_id;
};
PrintId print_id() const {
return PrintId{peer_id_short_};
}
private:
AdnlNodeIdShort peer_id_short_;
AdnlNodeIdFull peer_id_;
std::map<AdnlNodeIdShort, td::actor::ActorOwn<AdnlPeerPair>> peer_pairs_;
td::actor::ActorId<dht::Dht> dht_node_;
td::actor::ActorId<AdnlPeerTable> peer_table_;
td::actor::ActorId<AdnlNetworkManager> network_manager_;
};
} // namespace adnl
} // namespace ton
namespace td {
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::adnl::AdnlPeerImpl::PrintId &id) {
sb << "[peer " << id.peer_id << "]";
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::adnl::AdnlPeerImpl &peer) {
sb << peer.print_id();
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::adnl::AdnlPeerImpl *peer) {
sb << peer->print_id();
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::adnl::AdnlPeerPairImpl::PrintId &id) {
sb << "[peerpair " << id.peer_id << "-" << id.local_id << "]";
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::adnl::AdnlPeerPairImpl &peer) {
sb << peer.print_id();
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::adnl::AdnlPeerPairImpl *peer) {
sb << peer->print_id();
return sb;
}
} // namespace td

87
adnl/adnl-proxy-types.cpp Normal file
View file

@ -0,0 +1,87 @@
/*
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 "adnl-proxy-types.hpp"
#include "tl-utils/tl-utils.hpp"
#include "auto/tl/ton_api.hpp"
#include "td/utils/overloaded.h"
#include "td/utils/Time.h"
#include "common/errorcode.h"
namespace ton {
namespace adnl {
td::BufferSlice AdnlProxyFast::encrypt(Packet packet) const {
auto date = static_cast<td::uint32>(td::Clocks::system());
auto signature = create_hash_tl_object<ton_api::adnl_proxyToFastHash>(
packet.ip, packet.port, date, sha256_bits256(packet.data.as_slice()), shared_secret_);
auto obj = create_serialize_tl_object<ton_api::adnl_proxyToFast>(packet.ip, packet.port, date, signature);
td::BufferSlice res{32 + obj.size() + packet.data.size()};
auto S = res.as_slice();
S.copy_from(td::Bits256::zero().as_slice());
S.remove_prefix(32);
S.copy_from(obj.as_slice());
S.remove_prefix(obj.size());
S.copy_from(packet.data.as_slice());
return res;
}
td::Result<AdnlProxy::Packet> AdnlProxyFast::decrypt(td::BufferSlice packet) const {
if (packet.size() < 36) {
return td::Status::Error(ErrorCode::protoviolation, "too short packet");
}
td::Bits256 v;
v.as_slice().copy_from(packet.as_slice().truncate(32));
if (!v.is_zero()) {
return td::Status::Error(ErrorCode::protoviolation, "non-zero DST");
}
packet.confirm_read(32);
TRY_RESULT(R, fetch_tl_prefix<ton_api::adnl_proxyToFast>(packet, true));
if (R->date_ < td::Clocks::system() - 8) {
return td::Status::Error(ErrorCode::protoviolation, "too old date");
}
auto signature = create_hash_tl_object<ton_api::adnl_proxyToFastHash>(
R->ip_, R->port_, R->date_, sha256_bits256(packet.as_slice()), shared_secret_);
if (signature != R->signature_) {
return td::Status::Error(ErrorCode::protoviolation, "bad signature");
}
return Packet{static_cast<td::uint32>(R->ip_), static_cast<td::uint16>(R->port_), std::move(packet)};
}
td::Result<std::shared_ptr<AdnlProxy>> AdnlProxy::create(const ton_api::adnl_Proxy &proxy_type) {
std::shared_ptr<AdnlProxy> R;
ton_api::downcast_call(
const_cast<ton_api::adnl_Proxy &>(proxy_type),
td::overloaded([&](const ton_api::adnl_proxy_none &x) { R = std::make_shared<AdnlProxyNone>(); },
[&](const ton_api::adnl_proxy_fast &x) {
R = std::make_shared<AdnlProxyFast>(x.shared_secret_.as_slice());
}));
return R;
}
} // namespace adnl
} // namespace ton

45
adnl/adnl-proxy-types.h Normal file
View file

@ -0,0 +1,45 @@
/*
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/buffer.h"
#include "auto/tl/ton_api.h"
namespace ton {
namespace adnl {
class AdnlProxy {
public:
struct Packet {
td::uint32 ip;
td::uint16 port;
td::BufferSlice data;
};
virtual ~AdnlProxy() = default;
virtual td::BufferSlice encrypt(Packet packet) const = 0;
virtual td::Result<Packet> decrypt(td::BufferSlice packet) const = 0;
virtual tl_object_ptr<ton_api::adnl_Proxy> tl() const = 0;
static td::Result<std::shared_ptr<AdnlProxy>> create(const ton_api::adnl_Proxy &proxy_type);
};
} // namespace adnl
} // namespace ton

62
adnl/adnl-proxy-types.hpp Normal file
View file

@ -0,0 +1,62 @@
/*
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 "adnl-proxy-types.h"
#include "common/checksum.h"
namespace ton {
namespace adnl {
class AdnlProxyNone : public AdnlProxy {
public:
AdnlProxyNone() {
}
td::BufferSlice encrypt(Packet packet) const override {
return std::move(packet.data);
}
td::Result<Packet> decrypt(td::BufferSlice packet) const override {
return Packet{0, 0, std::move(packet)};
}
tl_object_ptr<ton_api::adnl_Proxy> tl() const override {
return create_tl_object<ton_api::adnl_proxy_none>();
}
};
class AdnlProxyFast : public AdnlProxy {
public:
AdnlProxyFast(td::Slice shared_secret)
: shared_secret_(sha256_bits256(shared_secret)), shared_secret_raw_(shared_secret) {
}
td::BufferSlice encrypt(Packet packet) const override;
td::Result<Packet> decrypt(td::BufferSlice packet) const override;
tl_object_ptr<ton_api::adnl_Proxy> tl() const override {
return create_tl_object<ton_api::adnl_proxy_fast>(shared_secret_raw_.clone_as_buffer_slice());
}
private:
td::Bits256 shared_secret_;
td::SharedSlice shared_secret_raw_;
};
} // namespace adnl
} // namespace ton

265
adnl/adnl-proxy.cpp Normal file
View file

@ -0,0 +1,265 @@
/*
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 "td/actor/actor.h"
#include "td/utils/buffer.h"
#include "td/utils/port/IPAddress.h"
#include "td/net/UdpServer.h"
#include "td/utils/port/signals.h"
#include "td/utils/OptionsParser.h"
#include "td/utils/FileLog.h"
#include "td/utils/port/path.h"
#include "td/utils/port/user.h"
#include "td/utils/filesystem.h"
#include "common/checksum.h"
#include "common/errorcode.h"
#include "tl-utils/tl-utils.hpp"
#include "auto/tl/ton_api_json.h"
#include "adnl-proxy-types.h"
#include <map>
#if TD_DARWIN || TD_LINUX
#include <unistd.h>
#endif
namespace ton {
namespace adnl {
class Receiver : public td::actor::Actor {
public:
void start_up() override;
void receive_common(td::BufferSlice data);
void receive_from_client(td::BufferSlice data);
void receive_to_client(td::BufferSlice data);
Receiver(td::uint16 in_port, td::uint16 out_port, std::shared_ptr<AdnlProxy> proxy, td::IPAddress client_addr)
: in_port_(in_port), out_port_(out_port), proxy_(std::move(proxy)), addr_(client_addr) {
}
private:
td::uint16 in_port_;
td::uint16 out_port_;
std::shared_ptr<ton::adnl::AdnlProxy> proxy_;
td::IPAddress addr_;
td::actor::ActorOwn<td::UdpServer> out_udp_server_;
td::actor::ActorOwn<td::UdpServer> in_udp_server_;
};
void Receiver::start_up() {
class Callback : public td::UdpServer::Callback {
public:
Callback(td::actor::ActorId<Receiver> manager, td::uint32 mode) : manager_(std::move(manager)), mode_(mode) {
}
private:
td::actor::ActorId<Receiver> manager_;
const td::uint32 mode_;
void on_udp_message(td::UdpMessage udp_message) override {
if (udp_message.error.is_error()) {
LOG(DEBUG) << udp_message.error;
return;
}
if (mode_ == 0) {
td::actor::send_closure_later(manager_, &Receiver::receive_common, std::move(udp_message.data));
} else if (mode_ == 1) {
td::actor::send_closure_later(manager_, &Receiver::receive_from_client, std::move(udp_message.data));
} else {
td::actor::send_closure_later(manager_, &Receiver::receive_to_client, std::move(udp_message.data));
}
}
};
if (in_port_ == out_port_) {
auto X = td::UdpServer::create("udp server", in_port_, std::make_unique<Callback>(actor_id(this), 0));
X.ensure();
in_udp_server_ = X.move_as_ok();
} else {
auto X = td::UdpServer::create("udp server", in_port_, std::make_unique<Callback>(actor_id(this), 1));
X.ensure();
in_udp_server_ = X.move_as_ok();
X = td::UdpServer::create("udp server", out_port_, std::make_unique<Callback>(actor_id(this), 2));
X.ensure();
out_udp_server_ = X.move_as_ok();
}
}
void Receiver::receive_common(td::BufferSlice data) {
if (data.size() <= 32) {
return;
}
td::Bits256 id;
id.as_slice().copy_from(data.as_slice().truncate(32));
if (id.is_zero()) {
receive_from_client(std::move(data));
} else {
receive_to_client(std::move(data));
}
}
void Receiver::receive_from_client(td::BufferSlice data) {
auto F = proxy_->decrypt(std::move(data));
if (F.is_error()) {
return;
}
auto f = F.move_as_ok();
td::IPAddress a;
if (a.init_ipv4_port(td::IPAddress::ipv4_to_str(f.ip), f.port).is_error()) {
return;
}
td::UdpMessage M;
M.address = a;
M.data = std::move(f.data);
td::actor::send_closure(out_udp_server_.empty() ? in_udp_server_.get() : out_udp_server_.get(), &td::UdpServer::send,
std::move(M));
}
void Receiver::receive_to_client(td::BufferSlice data) {
LOG(DEBUG) << "proxying to " << addr_;
td::UdpMessage M;
M.address = addr_;
M.data = std::move(data);
td::actor::send_closure(in_udp_server_.empty() ? out_udp_server_.get() : in_udp_server_.get(), &td::UdpServer::send,
std::move(M));
}
} // namespace adnl
} // namespace ton
int main(int argc, char *argv[]) {
SET_VERBOSITY_LEVEL(verbosity_INFO);
td::set_default_failure_signal_handler().ensure();
std::vector<td::actor::ActorOwn<ton::adnl::Receiver>> x;
std::unique_ptr<td::LogInterface> logger_;
SCOPE_EXIT {
td::log_interface = td::default_log_interface;
};
std::string config = "/var/ton-work/etc/adnl-proxy.conf.json";
td::OptionsParser p;
p.set_description("validator or full node for TON network");
p.add_option('v', "verbosity", "set verbosity level", [&](td::Slice arg) {
int v = VERBOSITY_NAME(FATAL) + (td::to_integer<int>(arg));
SET_VERBOSITY_LEVEL(v);
return td::Status::OK();
});
p.add_option('h', "help", "prints_help", [&]() {
char b[10240];
td::StringBuilder sb(td::MutableSlice{b, 10000});
sb << p;
std::cout << sb.as_cslice().c_str();
std::exit(2);
return td::Status::OK();
});
p.add_option('c', "config", "config file", [&](td::Slice arg) {
config = arg.str();
return td::Status::OK();
});
p.add_option('d', "daemonize", "set SIGHUP", [&]() {
td::set_signal_handler(td::SignalType::HangUp, [](int sig) {
#if TD_DARWIN || TD_LINUX
close(0);
setsid();
#endif
}).ensure();
return td::Status::OK();
});
#if TD_DARWIN || TD_LINUX
p.add_option('l', "logname", "log to file", [&](td::Slice fname) {
auto F = std::make_unique<td::FileLog>();
TRY_STATUS(F->init(fname.str()));
logger_ = std::move(F);
td::log_interface = logger_.get();
return td::Status::OK();
});
#endif
td::uint32 threads = 7;
p.add_option('t', "threads", PSTRING() << "number of threads (default=" << threads << ")", [&](td::Slice fname) {
td::int32 v;
try {
v = std::stoi(fname.str());
} catch (...) {
return td::Status::Error(ton::ErrorCode::error, "bad value for --threads: not a number");
}
if (v < 1 || v > 256) {
return td::Status::Error(ton::ErrorCode::error, "bad value for --threads: should be in range [1..256]");
}
threads = v;
return td::Status::OK();
});
p.add_option('u', "user", "change user", [&](td::Slice user) { return td::change_user(user); });
p.run(argc, argv).ensure();
td::actor::Scheduler scheduler({threads});
auto R = [&]() -> td::Status {
TRY_RESULT_PREFIX(conf_data, td::read_file(config), "failed to read: ");
TRY_RESULT_PREFIX(conf_json, td::json_decode(conf_data.as_slice()), "failed to parse json: ");
ton::ton_api::engine_adnlProxy_config conf;
TRY_STATUS_PREFIX(ton::ton_api::from_json(conf, conf_json.get_object()), "json does not fit TL scheme: ");
if (!conf.ports_.size()) {
return td::Status::Error("empty config");
}
for (auto &y : conf.ports_) {
auto in_port = static_cast<td::uint16>(y->in_port_);
auto out_port = static_cast<td::uint16>(y->out_port_);
if (!y->proxy_type_) {
return td::Status::Error("empty proxy type");
}
TRY_RESULT(proxy, ton::adnl::AdnlProxy::create(*y->proxy_type_.get()));
td::IPAddress a;
a.init_ipv4_port(td::IPAddress::ipv4_to_str(y->dst_ip_), static_cast<td::uint16>(y->dst_port_)).ensure();
scheduler.run_in_context([&] {
x.push_back(td::actor::create_actor<ton::adnl::Receiver>("adnl-proxy", in_port, out_port, std::move(proxy), a));
});
}
return td::Status::OK();
}();
if (R.is_error()) {
LOG(FATAL) << "bad config: " << R.move_as_error();
}
while (scheduler.run(1)) {
}
}

44
adnl/adnl-query.cpp Normal file
View file

@ -0,0 +1,44 @@
/*
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 "adnl-query.h"
#include "common/errorcode.h"
#include "td/utils/Random.h"
namespace ton {
namespace adnl {
void AdnlQuery::alarm() {
promise_.set_error(td::Status::Error(ErrorCode::timeout, "adnl query timeout"));
stop();
}
void AdnlQuery::result(td::BufferSlice data) {
promise_.set_value(std::move(data));
stop();
}
AdnlQueryId AdnlQuery::random_query_id() {
AdnlQueryId q_id;
td::Random::secure_bytes(q_id.as_slice());
return q_id;
}
} // namespace adnl
} // namespace ton

67
adnl/adnl-query.h Normal file
View file

@ -0,0 +1,67 @@
/*
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/actor/actor.h"
#include "common/bitstring.h"
#include "td/utils/buffer.h"
#include <functional>
namespace ton {
namespace adnl {
class AdnlPeerPair;
using AdnlQueryId = td::Bits256;
class AdnlQuery : public td::actor::Actor {
public:
static td::actor::ActorId<AdnlQuery> create(td::Promise<td::BufferSlice> promise,
std::function<void(AdnlQueryId)> destroy, std::string name,
td::Timestamp timeout, AdnlQueryId id) {
return td::actor::create_actor<AdnlQuery>("query", name, std::move(promise), std::move(destroy), timeout, id)
.release();
}
static AdnlQueryId random_query_id();
AdnlQuery(std::string name, td::Promise<td::BufferSlice> promise, std::function<void(AdnlQueryId)> destroy,
td::Timestamp timeout, AdnlQueryId id)
: name_(std::move(name)), timeout_(timeout), promise_(std::move(promise)), destroy_(std::move(destroy)), id_(id) {
}
void alarm() override;
void result(td::BufferSlice data);
void start_up() override {
alarm_timestamp() = timeout_;
}
void tear_down() override {
destroy_(id_);
}
private:
std::string name_;
td::Timestamp timeout_;
td::Promise<td::BufferSlice> promise_;
std::function<void(AdnlQueryId)> destroy_;
AdnlQueryId id_;
};
} // namespace adnl
} // namespace ton

View file

@ -0,0 +1,55 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "adnl-static-nodes.h"
#include "adnl-static-nodes.hpp"
#include "utils.hpp"
namespace ton {
namespace adnl {
void AdnlStaticNodesManagerImpl::add_node(AdnlNode node) {
auto id_short = node.compute_short_id();
VLOG(ADNL_INFO) << "[staticnodes] adding static node " << id_short;
nodes_.emplace(id_short, std::move(node));
}
void AdnlStaticNodesManagerImpl::del_node(AdnlNodeIdShort id) {
nodes_.erase(id);
}
td::Result<AdnlNode> AdnlStaticNodesManagerImpl::get_node(AdnlNodeIdShort id) {
auto it = nodes_.find(id);
if (it == nodes_.end()) {
return td::Status::Error(ErrorCode::notready, "static node not found");
}
return it->second;
}
td::actor::ActorOwn<AdnlStaticNodesManager> AdnlStaticNodesManager::create() {
auto X = td::actor::create_actor<AdnlStaticNodesManagerImpl>("staticnodesmanager");
return td::actor::ActorOwn<AdnlStaticNodesManager>(std::move(X));
}
} // namespace adnl
} // namespace ton

43
adnl/adnl-static-nodes.h Normal file
View file

@ -0,0 +1,43 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/actor/actor.h"
#include "td/utils/Status.h"
#include "td/actor/PromiseFuture.h"
#include "auto/tl/ton_api.h"
#include "adnl-peer-table.h"
namespace ton {
namespace adnl {
class AdnlStaticNodesManager : public td::actor::Actor {
public:
virtual void add_node(AdnlNode node) = 0;
virtual void del_node(AdnlNodeIdShort id) = 0;
virtual td::Result<AdnlNode> get_node(AdnlNodeIdShort id) = 0;
static td::actor::ActorOwn<AdnlStaticNodesManager> create();
};
} // namespace adnl
} // namespace ton

View file

@ -0,0 +1,42 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <map>
#include "adnl-static-nodes.h"
namespace ton {
namespace adnl {
class AdnlStaticNodesManagerImpl : public AdnlStaticNodesManager {
public:
void add_node(AdnlNode node) override;
void del_node(AdnlNodeIdShort id) override;
td::Result<AdnlNode> get_node(AdnlNodeIdShort id) override;
AdnlStaticNodesManagerImpl() {
}
private:
std::map<AdnlNodeIdShort, AdnlNode> nodes_;
};
} // namespace adnl
} // namespace ton

View file

@ -0,0 +1,38 @@
/*
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 "adnl-test-loopback-implementation.h"
namespace ton {
namespace adnl {
AdnlAddressList TestLoopbackNetworkManager::generate_dummy_addr_list(bool empty) {
auto obj = ton::create_tl_object<ton::ton_api::adnl_address_udp>(1, 1);
auto objv = std::vector<ton::tl_object_ptr<ton::ton_api::adnl_Address>>();
objv.push_back(std::move(obj));
td::uint32 now = Adnl::adnl_start_time();
auto addrR = ton::adnl::AdnlAddressList::create(
ton::create_tl_object<ton::ton_api::adnl_addressList>(std::move(objv), empty ? 0 : now, empty ? 0 : now, 0, 0));
addrR.ensure();
return addrR.move_as_ok();
}
} // namespace adnl
} // namespace ton

View file

@ -0,0 +1,86 @@
/*
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 "adnl/adnl.h"
#include "td/utils/Random.h"
#include <set>
namespace ton {
namespace adnl {
class TestLoopbackNetworkManager : public ton::adnl::AdnlNetworkManager {
public:
void install_callback(std::unique_ptr<Callback> callback) override {
CHECK(!callback_);
callback_ = std::move(callback);
}
void add_self_addr(td::IPAddress addr, td::uint32 priority) override {
}
void add_proxy_addr(td::IPAddress addr, std::shared_ptr<AdnlProxy> proxy, td::uint32 priority) override {
}
void send_udp_packet(ton::adnl::AdnlNodeIdShort src_id, ton::adnl::AdnlNodeIdShort dst_id, td::IPAddress dst_addr,
td::uint32 priority, td::BufferSlice data) override {
if (allowed_sources_.count(src_id) == 0 || allowed_destinations_.count(dst_id) == 0) {
// just drop
return;
}
if (loss_probability_ > 0 && td::Random::fast(0, 10000) < loss_probability_ * 10000) {
return;
}
CHECK(callback_);
callback_->receive_packet(dst_addr, std::move(data));
}
void add_node_id(AdnlNodeIdShort id, bool allow_send, bool allow_receive) {
if (allow_send) {
allowed_sources_.insert(id);
} else {
allowed_sources_.erase(id);
}
if (allow_receive) {
allowed_destinations_.insert(id);
} else {
allowed_destinations_.erase(id);
}
}
void set_loss_probability(double p) {
CHECK(p >= 0 && p <= 1);
loss_probability_ = p;
}
TestLoopbackNetworkManager() {
}
static AdnlAddressList generate_dummy_addr_list(bool empty = false);
private:
std::set<AdnlNodeIdShort> allowed_sources_;
std::set<AdnlNodeIdShort> allowed_destinations_;
std::unique_ptr<Callback> callback_;
double loss_probability_ = 0.0;
};
} // namespace adnl
} // namespace ton

120
adnl/adnl.h Normal file
View file

@ -0,0 +1,120 @@
/*
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/actor/actor.h"
#include "auto/tl/ton_api.h"
#include "td/utils/port/IPAddress.h"
#include "adnl-node-id.hpp"
#include "adnl-node.h"
#include "common/errorcode.h"
#include "keyring/keyring.h"
namespace ton {
namespace dht {
class Dht;
}
namespace adnl {
class AdnlNetworkManager;
class AdnlExtServer : public td::actor::Actor {
public:
virtual void add_local_id(AdnlNodeIdShort id) = 0;
virtual void add_tcp_port(td::uint16 port) = 0;
virtual ~AdnlExtServer() = default;
};
class AdnlSenderInterface : public td::actor::Actor {
public:
virtual ~AdnlSenderInterface() = default;
virtual void send_message(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data) = 0;
virtual void send_query(AdnlNodeIdShort src, AdnlNodeIdShort dst, std::string name,
td::Promise<td::BufferSlice> promise, td::Timestamp timeout, td::BufferSlice data) = 0;
virtual void send_query_ex(AdnlNodeIdShort src, AdnlNodeIdShort dst, std::string name,
td::Promise<td::BufferSlice> promise, td::Timestamp timeout, td::BufferSlice data,
td::uint64 max_answer_size) = 0;
};
class Adnl : public AdnlSenderInterface {
public:
class Callback {
public:
virtual void receive_message(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data) = 0;
virtual void receive_query(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data,
td::Promise<td::BufferSlice> promise) = 0;
virtual ~Callback() = default;
};
static constexpr td::uint32 get_mtu() {
return 1024;
}
static constexpr td::uint32 huge_packet_max_size() {
return 1024 * 8;
}
// adds node to peer table
// used mostly from DHT to avoid loops
virtual void add_peer(AdnlNodeIdShort local_id, AdnlNodeIdFull id, AdnlAddressList addr_list) = 0;
// adds address list for nodes from config
virtual void add_static_nodes_from_config(AdnlNodesList nodes) = 0;
// adds local id. After that you can send/receive messages from/to this id
virtual void add_id(AdnlNodeIdFull id, AdnlAddressList addr_list) = 0;
virtual void del_id(AdnlNodeIdShort id, td::Promise<td::Unit> promise) = 0;
// subscribe to (some) messages(+queries) to this local id
virtual void subscribe(AdnlNodeIdShort dst, std::string prefix, std::unique_ptr<Callback> callback) = 0;
virtual void unsubscribe(AdnlNodeIdShort dst, std::string prefix) = 0;
// register (main) dht node
// it will be used to send queries to DHT from adnl
// there are two types of queries:
// - discover node addr list for unknown node
// - update local node information
virtual void register_dht_node(td::actor::ActorId<dht::Dht> dht_node) = 0;
virtual void register_network_manager(td::actor::ActorId<AdnlNetworkManager> network_manager) = 0;
// get local id information
// for example when you need to sent it further
virtual void get_addr_list(AdnlNodeIdShort id, td::Promise<AdnlAddressList> promise) = 0;
virtual void get_self_node(AdnlNodeIdShort id, td::Promise<AdnlNode> promise) = 0;
virtual void create_ext_server(std::vector<AdnlNodeIdShort> ids, std::vector<td::uint16> ports,
td::Promise<td::actor::ActorOwn<AdnlExtServer>> promise) = 0;
static td::actor::ActorOwn<Adnl> create(std::string db, td::actor::ActorId<keyring::Keyring> keyring);
static std::string int_to_bytestring(td::int32 id) {
return std::string(reinterpret_cast<char *>(&id), 4);
}
static td::int32 adnl_start_time();
};
} // namespace adnl
using Adnl = adnl::Adnl;
} // namespace ton

View file

@ -0,0 +1,240 @@
/*
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 "adnl/adnl-network-manager.h"
#include "adnl/adnl-peer-table.h"
#include "adnl/utils.hpp"
#include "keys/encryptor.h"
#include "td/utils/Time.h"
#include "td/utils/format.h"
#include "td/utils/OptionsParser.h"
#include <iostream>
#include <sstream>
template <std::size_t size>
std::ostream &operator<<(std::ostream &stream, const td::UInt<size> &x) {
for (size_t i = 0; i < size / 8; i++) {
stream << td::format::hex_digit((x.raw[i] >> 4) & 15) << td::format::hex_digit(x.raw[i] & 15);
}
return stream;
}
class AdnlNode : public td::actor::Actor {
private:
std::vector<td::UInt256> ping_ids_;
td::actor::ActorOwn<ton::AdnlNetworkManager> network_manager_;
td::actor::ActorOwn<ton::AdnlPeerTable> peer_table_;
td::UInt256 local_id_;
bool local_id_set_ = false;
std::string host_ = "127.0.0.1";
td::uint32 ip_ = 0x7f000001;
td::uint16 port_ = 2380;
void receive_message(td::UInt256 src, td::UInt256 dst, td::BufferSlice data) {
std::cout << "MESSAGE FROM " << src << " to " << dst << " of size " << std::to_string(data.size()) << "\n";
}
void receive_query(td::UInt256 src, td::UInt256 dst, td::uint64 query_id, td::BufferSlice data) {
std::cout << "QUERY " << std::to_string(query_id) << " FROM " << src << " to " << dst << " of size "
<< std::to_string(data.size()) << "\n";
td::actor::send_closure(peer_table_, &ton::AdnlPeerTable::answer_query, dst, src, query_id,
ton::create_tl_object<ton::ton_api::testObject>());
}
std::unique_ptr<ton::AdnlPeerTable::Callback> make_callback() {
class Callback : public ton::AdnlPeerTable::Callback {
public:
void receive_message(td::UInt256 src, td::UInt256 dst, td::BufferSlice data) override {
td::actor::send_closure(id_, &AdnlNode::receive_message, src, dst, std::move(data));
}
void receive_query(td::UInt256 src, td::UInt256 dst, td::uint64 query_id, td::BufferSlice data) override {
td::actor::send_closure(id_, &AdnlNode::receive_query, src, dst, query_id, std::move(data));
}
Callback(td::actor::ActorId<AdnlNode> id) : id_(std::move(id)) {
}
private:
td::actor::ActorId<AdnlNode> id_;
};
return std::make_unique<Callback>(td::actor::actor_id(this));
}
public:
void start_up() override {
alarm_timestamp() = td::Timestamp::in(1);
}
AdnlNode() {
network_manager_ = ton::AdnlNetworkManager::create();
peer_table_ = ton::AdnlPeerTable::create();
td::actor::send_closure(network_manager_, &ton::AdnlNetworkManager::register_peer_table, peer_table_.get());
td::actor::send_closure(peer_table_, &ton::AdnlPeerTable::register_network_manager, network_manager_.get());
}
void listen_udp(td::uint16 port) {
td::actor::send_closure(network_manager_, &ton::AdnlNetworkManager::add_listening_udp_port, "0.0.0.0", port);
port_ = port;
}
void set_host(td::IPAddress ip, std::string host) {
ip_ = ip.get_ipv4();
host_ = host;
}
void send_pings_to(td::UInt256 id) {
std::cout << "send pings to " << id << "\n";
ping_ids_.push_back(id);
}
void add_local_id(ton::tl_object_ptr<ton::ton_api::adnl_id_Pk> pk_) {
auto pub_ = ton::get_public_key(pk_);
local_id_ = ton::adnl_short_id(pub_);
std::cout << "local_id = '" << local_id_ << "'\n";
auto x = ton::create_tl_object<ton::ton_api::adnl_address_udp>(ip_, port_);
auto v = std::vector<ton::tl_object_ptr<ton::ton_api::adnl_Address>>();
v.push_back(ton::move_tl_object_as<ton::ton_api::adnl_Address>(x));
auto y =
ton::create_tl_object<ton::ton_api::adnl_addressList>(std::move(v), static_cast<td::int32>(td::Time::now()));
LOG(INFO) << "local_addr_list: " << ton::ton_api::to_string(y);
td::actor::send_closure(peer_table_, &ton::AdnlPeerTable::add_id, std::move(pk_), std::move(y));
td::actor::send_closure(peer_table_, &ton::AdnlPeerTable::subscribe, local_id_, "", make_callback());
local_id_set_ = true;
}
void add_foreign(ton::tl_object_ptr<ton::ton_api::adnl_id_Full> id,
ton::tl_object_ptr<ton::ton_api::adnl_addressList> addr_list) {
std::cout << ton::adnl_short_id(id) << "\n";
td::actor::send_closure(peer_table_, &ton::AdnlPeerTable::add_peer, std::move(id), std::move(addr_list));
}
void alarm() override {
std::cout << "alarm\n";
if (local_id_set_) {
for (auto it = ping_ids_.begin(); it != ping_ids_.end(); it++) {
auto P = td::PromiseCreator::lambda([](td::Result<td::BufferSlice> result) {
if (result.is_error()) {
std::cout << "received error " << result.move_as_error().to_string() << "\n";
} else {
auto message = result.move_as_ok();
std::cout << "received answer to query\n";
}
});
td::actor::send_closure(peer_table_, &ton::AdnlPeerTable::send_query, local_id_, *it, std::move(P),
td::Timestamp::in(5), ton::create_tl_object<ton::ton_api::getTestObject>());
}
}
alarm_timestamp() = td::Timestamp::in(1);
}
};
td::Result<td::UInt256> get_uint256(std::string str) {
if (str.size() != 64) {
return td::Status::Error("uint256 must have 64 bytes");
}
td::UInt256 res;
for (size_t i = 0; i < 32; i++) {
res.raw[i] = static_cast<td::uint8>(td::hex_to_int(str[2 * i]) * 16 + td::hex_to_int(str[2 * i + 1]));
}
return res;
}
int main(int argc, char *argv[]) {
td::actor::ActorOwn<AdnlNode> x;
td::OptionsParser p;
p.set_description("test basic adnl functionality");
p.add_option('h', "help", "prints_help", [&]() {
char b[10240];
td::StringBuilder sb({b, 10000});
sb << p;
std::cout << sb.as_cslice().c_str();
std::exit(2);
return td::Status::OK();
});
p.add_option('p', "port", "sets udp port", [&](td::Slice port) {
td::actor::send_closure(x, &AdnlNode::listen_udp, static_cast<td::uint16>(std::stoi(port.str())));
return td::Status::OK();
});
p.add_option('a', "host", "sets local ip", [&](td::Slice ip) {
td::IPAddress addr;
auto R = addr.init_host_port(ip.str(), 0);
if (R.is_error()) {
return R;
}
td::actor::send_closure(x, &AdnlNode::set_host, addr, ip.str());
return td::Status::OK();
});
p.add_option('i', "id", "sets local id", [&](td::Slice id) {
td::actor::send_closure(x, &AdnlNode::add_local_id,
ton::create_tl_object<ton::ton_api::adnl_id_pk_unenc>(id.str()));
return td::Status::OK();
});
p.add_option('P', "peer", "adds peer id@host:port", [&](td::Slice id) {
auto pos = id.rfind('@');
if (pos == static_cast<size_t>(-1)) {
return td::Status::Error("--peer expected randomtag@host:port as argument");
}
auto s1 = id.substr(0, pos);
auto f_id = ton::create_tl_object<ton::ton_api::adnl_id_unenc>(s1.str());
td::IPAddress addr;
auto R = addr.init_host_port(td::CSlice(id.substr(pos + 1).str()));
if (R.is_error()) {
return R.move_as_error();
}
auto f_addr = ton::create_tl_object<ton::ton_api::adnl_address_udp>(addr.get_ipv4(), addr.get_port());
std::vector<ton::tl_object_ptr<ton::ton_api::adnl_Address>> vv;
vv.push_back(ton::move_tl_object_as<ton::ton_api::adnl_Address>(f_addr));
auto f_addr_list =
ton::create_tl_object<ton::ton_api::adnl_addressList>(std::move(vv), static_cast<int>(td::Time::now()));
td::actor::send_closure(x, &AdnlNode::add_foreign, ton::move_tl_object_as<ton::ton_api::adnl_id_Full>(f_id),
std::move(f_addr_list));
return td::Status::OK();
});
p.add_option('n', "node", "node to send pings to", [&](td::Slice node) {
auto R = get_uint256(node.str());
if (R.is_error()) {
return R.move_as_error();
}
td::actor::send_closure(x, &AdnlNode::send_pings_to, R.move_as_ok());
return td::Status::OK();
});
td::actor::Scheduler scheduler({2});
scheduler.run_in_context([&] {
x = td::actor::create_actor<AdnlNode>(td::actor::ActorInfoCreator::Options().with_name("A").with_poll());
});
scheudler.run();
return 0;
}

27
adnl/utils.cpp Normal file
View file

@ -0,0 +1,27 @@
/*
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.hpp"
#include "tl/tl_object_store.h"
#include "td/utils/format.h"
#include "td/utils/Random.h"
#include "keys/encryptor.h"
#include "auto/tl/ton_api.hpp"
#include "adnl-node-id.hpp"
namespace ton {} // namespace ton

45
adnl/utils.hpp Normal file
View file

@ -0,0 +1,45 @@
/*
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/buffer.h"
#include "td/utils/misc.h"
#include "td/utils/crypto.h"
#include "td/utils/format.h"
#include "td/utils/base64.h"
#include "tl-utils/tl-utils.hpp"
#include "common/errorcode.h"
#include "common/checksum.h"
#include "adnl-node-id.hpp"
#include "common/status.h"
#include "adnl-node.h"
#include "adnl-address-list.hpp"
namespace ton {
namespace adnl {
inline bool adnl_node_is_older(AdnlNode &a, AdnlNode &b) {
return a.addr_list().version() < b.addr_list().version();
}
} // namespace adnl
} // namespace ton

View file

@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
find_package(MHD)
if (MHD_FOUND)
set(BLOCHAIN_EXPLORER_SOURCE
blockchain-explorer.cpp
blockchain-explorer.hpp
blockchain-explorer-http.cpp
blockchain-explorer-http.hpp
blockchain-explorer-query.cpp
blockchain-explorer-query.hpp
)
add_executable(blockchain-explorer ${BLOCHAIN_EXPLORER_SOURCE})
target_include_directories(blockchain-explorer PUBLIC ${MHD_INCLUDE_DIRS})
target_link_libraries(blockchain-explorer tdutils tdactor adnllite tl_lite_api tl-lite-utils
ton_crypto ton_block ${MHD_LIBRARY})
endif()

View file

@ -0,0 +1,695 @@
/*
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 "blockchain-explorer-http.hpp"
#include "block/block-db.h"
#include "block/block.h"
#include "block/block-parse.h"
#include "block/block-auto.h"
#include "vm/boc.h"
#include "vm/cellops.h"
#include "vm/cells/MerkleProof.h"
#include "block/mc-config.h"
#include "ton/ton-shard.h"
HttpAnswer& HttpAnswer::operator<<(AddressCell addr_c) {
ton::WorkchainId wc;
ton::StdSmcAddress addr;
if (!block::tlb::t_MsgAddressInt.extract_std_address(addr_c.root, wc, addr)) {
abort("<cannot unpack addr>");
return *this;
}
block::StdAddress caddr{wc, addr};
*this << "<a href=\"" << AccountLink{caddr, ton::BlockIdExt{}} << "\">" << caddr.rserialize(true) << "</a>";
return *this;
}
HttpAnswer& HttpAnswer::operator<<(MessageCell msg) {
if (msg.root.is_null()) {
abort("<message not found");
return *this;
}
vm::CellSlice cs{vm::NoVmOrd(), msg.root};
block::gen::CommonMsgInfo info;
td::Ref<vm::CellSlice> src, dest;
*this << "<div id=\"msg" << msg.root->get_hash() << "\">";
*this << "<div class=\"table-responsive my-3\">\n"
<< "<table class=\"table-sm table-striped\">\n"
<< "<tr><th>hash</th><td>" << msg.root->get_hash().to_hex() << "</td></tr>\n";
switch (block::gen::t_CommonMsgInfo.get_tag(cs)) {
case block::gen::CommonMsgInfo::ext_in_msg_info: {
block::gen::CommonMsgInfo::Record_ext_in_msg_info info;
if (!tlb::unpack(cs, info)) {
abort("<cannot unpack inbound external message>");
return *this;
}
*this << "<tr><th>type</th><td>external</td></tr>\n"
<< "<tr><th>source</th><td>NONE</td></tr>\n"
<< "<tr><th>destination</th><td>" << AddressCell{info.dest} << "</td></tr>\n";
break;
}
case block::gen::CommonMsgInfo::ext_out_msg_info: {
block::gen::CommonMsgInfo::Record_ext_out_msg_info info;
if (!tlb::unpack(cs, info)) {
abort("<cannot unpack outbound external message>");
return *this;
}
*this << "<tr><th>type</th><td>external OUT</td></tr>\n"
<< "<tr><th>source</th><td>" << AddressCell{info.src} << "</td></tr>\n"
<< "<tr><th>destination</th><td>NONE</td></tr>\n"
<< "<tr><th>lt</th><td>" << info.created_lt << "</td></tr>\n"
<< "<tr><th>time</th><td>" << info.created_at << "</td></tr>\n";
break;
}
case block::gen::CommonMsgInfo::int_msg_info: {
block::gen::CommonMsgInfo::Record_int_msg_info info;
if (!tlb::unpack(cs, info)) {
abort("cannot unpack internal message");
return *this;
}
td::RefInt256 value;
td::Ref<vm::Cell> extra;
if (!block::unpack_CurrencyCollection(info.value, value, extra)) {
abort("cannot unpack message value");
return *this;
}
*this << "<tr><th>type</th><td>internal</td></tr>\n"
<< "<tr><th>source</th><td>" << AddressCell{info.src} << "</td></tr>\n"
<< "<tr><th>destination</th><td>" << AddressCell{info.dest} << "</td></tr>\n"
<< "<tr><th>lt</th><td>" << info.created_lt << "</td></tr>\n"
<< "<tr><th>time</th><td>" << info.created_at << "</td></tr>\n"
<< "<tr><th>value</th><td>" << value << "</td></tr>\n";
break;
}
default:
abort("cannot unpack message");
return *this;
}
*this << "</table></div>\n";
*this << RawData<block::gen::Message>{msg.root, block::gen::t_Anything} << "</div>";
return *this;
}
HttpAnswer& HttpAnswer::operator<<(ton::BlockIdExt block_id) {
return *this << "<a href=\"" << BlockLink{block_id} << "\">" << block_id.id.to_str() << "</a>";
}
HttpAnswer& HttpAnswer::operator<<(ton::BlockId block_id) {
return *this << "<a href=\"" << prefix_ << "search?workchain=" << block_id.workchain
<< "&shard=" << ton::shard_to_str(block_id.shard) << "&seqno=" << block_id.seqno << "\">"
<< block_id.to_str() << "</a>";
}
HttpAnswer& HttpAnswer::operator<<(BlockSearch bs) {
*this << "<form class=\"container\" action=\"" << prefix_ << "search\" method=\"get\">"
<< "<div class=\"row\">"
<< "<div class=\"form-group col-lg-3 col-md-4\">"
<< "<label>workchain</label>"
<< "<input type=\"text\" class=\"form-control mr-2\" name=\"workchain\" value=\""
<< (bs.block_id.is_valid() ? std::to_string(bs.block_id.id.workchain) : "") << "\">"
<< "</div>\n"
<< "<div class=\"form-group col-lg-3 col-md-4\">"
<< "<label>shard/account</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"shard\" value=\""
<< (bs.block_id.is_valid() ? ton::shard_to_str(bs.block_id.id.shard) : "") << "\"></div>"
<< "<div class=\"form-group col-lg-3 col-md-4\">"
<< "<label>seqno</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"seqno\" value=\""
<< (bs.block_id.is_valid() ? std::to_string(bs.block_id.id.seqno) : "") << "\"></div>"
<< "<div class=\"form-group col-lg-3 col-md-4\">"
<< "<label class=\"d-none d-lg-block\">&nbsp;</label>"
<< "<div><button type=\"submit\" class=\"btn btn-primary mr-2\">Submit</button></div>"
<< "</div></div><div class=\"row\">"
<< "<div class=\"form-group col-md-6\">"
<< "<label>logical time</label>"
<< "<input type=\"text\" class=\"form-control mr-2\" name=\"lt\">"
<< "</div>\n"
<< "<div class=\"form-group col-md-6\">"
<< "<label>unix time</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"utime\"></div>"
<< "</div><div class=\"row\">"
<< "<div class=\"form-group col-md-6\">"
<< "<label>root hash</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"roothash\" value=\""
//<< (!bs.block_id.id.is_valid() || bs.block_id.root_hash.is_zero() ? "" : bs.block_id.root_hash.to_hex())
<< "\"></div>"
<< "<div class=\"col-md-6\">"
<< "<label>file hash</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"filehash\" value=\""
//<< (!bs.block_id.id.is_valid() || bs.block_id.file_hash.is_zero() ? "" : bs.block_id.file_hash.to_hex())
<< "\"></div>"
<< "</div></form>\n";
return *this;
}
HttpAnswer& HttpAnswer::operator<<(AccountSearch bs) {
*this << "<form class=\"container\" action=\"" << prefix_ << "account\" method=\"get\">"
<< "<div class=\"row\">"
<< "<div class=\"form-group col-lg-3 col-md-4\">"
<< "<label>workchain</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"workchain\" value=\""
<< (bs.block_id.is_valid() ? std::to_string(bs.block_id.id.workchain) : "") << "\"></div>"
<< "<div class=\"form-group col-lg-3 col-md-4\">"
<< "<label>shard</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"shard\" value=\""
<< (bs.block_id.is_valid() ? ton::shard_to_str(bs.block_id.id.shard) : "") << "\"></div>"
<< "<div class=\"form-group col-lg-3 col-md-4\">"
<< "<label>seqno</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"seqno\" value=\""
<< (bs.block_id.is_valid() ? std::to_string(bs.block_id.id.seqno) : "") << "\"></div>"
<< "<div class=\"form-group col-lg-3 col-md-4\">"
<< "<label class=\"d-none d-lg-block\">&nbsp;</label>"
<< "<div><button type=\"submit\" class=\"btn btn-primary mr-2\">Submit</button>"
<< "<button class=\"btn btn-outline-primary\" type=\"reset\">Reset</button></div>"
<< "</div></div><div class=\"row\">"
<< "<div class=\"form-group col-md-6\">"
<< "<label>root hash</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"roothash\" value=\""
<< (!bs.block_id.id.is_valid() || bs.block_id.root_hash.is_zero() ? "" : bs.block_id.root_hash.to_hex())
<< "\"></div>"
<< "<div class=\"form-group col-md-6\">"
<< "<label>file hash</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"filehash\" value=\""
<< (!bs.block_id.id.is_valid() || bs.block_id.file_hash.is_zero() ? "" : bs.block_id.file_hash.to_hex())
<< "\"></div>"
<< "</div><div class=\"row\">"
<< "<div class=\"form-group col-md-12\">"
<< "<label>account id</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"account\" value=\""
<< (bs.addr.addr.is_zero() ? "" : bs.addr.rserialize(true)) << "\"></div>"
<< "</div>\n"
<< "</form>\n";
return *this;
}
HttpAnswer& HttpAnswer::operator<<(TransactionSearch bs) {
*this << "<form class=\"container\" action=\"" << prefix_ << "transaction\" method=\"get\">"
<< "<div class=\"row\">"
<< "<div class=\"form-group col-lg-3 col-md-4\">"
<< "<label>workchain</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"workchain\" value=\""
<< (bs.block_id.is_valid() ? std::to_string(bs.block_id.id.workchain) : "") << "\"></div>"
<< "<div class=\"form-group col-lg-3 col-md-4\">"
<< "<label>shard</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"shard\" value=\""
<< (bs.block_id.is_valid() ? ton::shard_to_str(bs.block_id.id.shard) : "") << "\"></div>"
<< "<div class=\"form-group col-lg-3 col-md-4\">"
<< "<label>seqno</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"seqno\" value=\""
<< (bs.block_id.is_valid() ? std::to_string(bs.block_id.id.seqno) : "") << "\"></div>"
<< "<div class=\"form-group col-lg-3 col-md-4\">"
<< "<label class=\"d-none d-lg-block\">&nbsp;</label>"
<< "<div><button type=\"submit\" class=\"btn btn-primary mr-2\">Submit</button>"
<< "<button class=\"btn btn-outline-primary\" type=\"reset\">Reset</button></div>"
<< "</div></div><div class=\"row\">"
<< "<div class=\"form-group col-md-6\">"
<< "<label>root hash</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"roothash\" value=\""
<< (!bs.block_id.id.is_valid() || bs.block_id.root_hash.is_zero() ? "" : bs.block_id.root_hash.to_hex())
<< "\"></div>"
<< "<div class=\"form-group col-md-6\">"
<< "<label>file hash</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"filehash\" value=\""
<< (!bs.block_id.id.is_valid() || bs.block_id.file_hash.is_zero() ? "" : bs.block_id.file_hash.to_hex())
<< "\"></div>"
<< "</div><div class=\"row\">"
<< "<div class=\"form-group col-md-12\">"
<< "<label>account id</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"account\" value=\""
<< (bs.addr.addr.is_zero() ? "" : bs.addr.rserialize(true)) << "\"></div>"
<< "</div><div class=\"row\">"
<< "<div class=\"form-group col-md-3\">"
<< "<label>transaction lt</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"lt\" value=\""
<< (bs.lt ? std::to_string(bs.lt) : "") << "\"></div>"
<< "<div class=\"form-group col-md-9\">"
<< "<label>transaction hash</label>"
<< "<input type =\"text\" class=\"form-control mr-2\" name=\"hash\" value=\""
<< (bs.hash.is_zero() ? "" : bs.hash.to_hex()) << "\"></div>"
<< "</div>\n"
<< "</form>\n";
return *this;
}
HttpAnswer& HttpAnswer::operator<<(TransactionCell trans_c) {
if (trans_c.root.is_null()) {
abort("transaction not found");
return *this;
}
block::gen::Transaction::Record trans;
if (!tlb::unpack_cell(trans_c.root, trans)) {
abort("cannot unpack");
return *this;
}
*this << "<div class=\"table-responsive my-3\">\n"
<< "<table class=\"table-sm table-striped\">\n"
<< "<tr><th>block</th><td><a href=\"" << BlockLink{trans_c.block_id} << "\">"
<< trans_c.block_id.id.to_str() << "</a></td></tr>"
<< "<tr><th>workchain</th><td>" << trans_c.addr.workchain << "</td></tr>"
<< "<tr><th>account hex</th><td>" << trans_c.addr.addr.to_hex() << "</td></tr>"
<< "<tr><th>account</th><td>" << trans_c.addr.rserialize(true) << "</td></tr>"
<< "<tr><th>hash</th><td>" << trans_c.root->get_hash().to_hex() << "</td></tr>\n"
<< "<tr><th>lt</th><td>" << trans.lt << "</td></tr>\n"
<< "<tr><th>time</th><td>" << trans.now << "</td></tr>\n"
<< "<tr><th>out messages</th><td>";
vm::Dictionary dict{trans.r1.out_msgs, 15};
for (td::int32 i = 0; i < trans.outmsg_cnt; i++) {
auto out_msg = dict.lookup_ref(td::BitArray<15>{i});
*this << " <a href=\"" << MessageLink{out_msg} << "\">" << i << "</a>";
}
*this << "</td></tr>\n"
<< "<tr><th>in message</th><td>";
auto in_msg = trans.r1.in_msg->prefetch_ref();
if (in_msg.is_null()) {
*this << "NONE";
} else {
*this << "<a href=\"" << MessageLink{in_msg} << "\">" << in_msg->get_hash() << "</a>";
}
*this << "</td></tr>\n"
<< "<tr><th>prev transaction</th><td>";
auto prev_lt = trans.prev_trans_lt;
auto prev_hash = trans.prev_trans_hash;
if (prev_lt > 0) {
*this << "<a href=\"" << TransactionLink{trans_c.addr, prev_lt, prev_hash} << "\">lt=" << prev_lt
<< " hash=" << prev_hash.to_hex() << "</a>";
} else {
*this << "NONE";
}
*this << "</td></tr></table></div>\n";
if (in_msg.not_null()) {
*this << "<hr />" << MessageCell{in_msg};
}
for (int x = 0; x < trans.outmsg_cnt && x < 100; x++) {
auto out_msg = dict.lookup_ref(td::BitArray<15>{x});
*this << "<hr />" << MessageCell{out_msg};
}
*this << "<hr />";
return *this << RawData<block::gen::Transaction>{trans_c.root} << "</div>";
}
HttpAnswer& HttpAnswer::operator<<(AccountCell acc_c) {
*this << "<div>";
auto block_id = acc_c.block_id;
if (!block_id.is_valid_full()) {
abort(PSTRING() << "shard block id " << block_id.to_str() << " in answer is invalid");
return *this;
}
if (!ton::shard_contains(block_id.shard_full(), ton::extract_addr_prefix(acc_c.addr.workchain, acc_c.addr.addr))) {
abort(PSTRING() << "received data from shard block " << block_id.to_str()
<< " that cannot contain requested account " << acc_c.addr.workchain << ":"
<< acc_c.addr.addr.to_hex());
return *this;
}
if (acc_c.q_roots.size() != 2) {
abort(PSTRING() << "account state proof must have exactly two roots");
return *this;
}
ton::LogicalTime last_trans_lt = 0;
ton::Bits256 last_trans_hash;
last_trans_hash.set_zero();
try {
auto state_root = vm::MerkleProof::virtualize(acc_c.q_roots[1], 1);
if (state_root.is_null()) {
abort("account state proof is invalid");
return *this;
}
block::gen::ShardStateUnsplit::Record sstate;
if (!(tlb::unpack_cell(std::move(state_root), sstate))) {
abort("cannot unpack state header");
return *this;
}
vm::AugmentedDictionary accounts_dict{vm::load_cell_slice_ref(sstate.accounts), 256, block::tlb::aug_ShardAccounts};
auto acc_csr = accounts_dict.lookup(acc_c.addr.addr);
if (acc_csr.not_null()) {
if (acc_c.root.is_null()) {
abort(PSTRING() << "account state proof shows that account state for " << acc_c.addr.workchain << ":"
<< acc_c.addr.addr.to_hex() << " must be non-empty, but it actually is empty");
return *this;
}
block::gen::ShardAccount::Record acc_info;
if (!tlb::csr_unpack(std::move(acc_csr), acc_info)) {
abort("cannot unpack ShardAccount from proof");
return *this;
}
if (acc_info.account->get_hash().bits().compare(acc_c.root->get_hash().bits(), 256)) {
abort(PSTRING() << "account state hash mismatch: Merkle proof expects "
<< acc_info.account->get_hash().bits().to_hex(256) << " but received data has "
<< acc_c.root->get_hash().bits().to_hex(256));
return *this;
}
last_trans_hash = acc_info.last_trans_hash;
last_trans_lt = acc_info.last_trans_lt;
} else if (acc_c.root.not_null()) {
abort(PSTRING() << "account state proof shows that account state for " << acc_c.addr.workchain << ":"
<< acc_c.addr.addr.to_hex() << " must be empty, but it is not");
return *this;
}
} catch (vm::VmError err) {
abort(PSTRING() << "error while traversing account proof : " << err.get_msg());
return *this;
} catch (vm::VmVirtError err) {
abort(PSTRING() << "virtualization error while traversing account proof : " << err.get_msg());
return *this;
}
*this << "<div class=\"table-responsive my-3\">\n"
<< "<table class=\"table-sm table-striped\">\n";
*this << "<tr><th>block</th><td><a href=\"" << BlockLink{acc_c.block_id} << "\">"
<< block_id.id.to_str() << "</a></td></tr>";
*this << "<tr><th>workchain</th><td>" << acc_c.addr.workchain << "</td></tr>";
*this << "<tr><th>account hex</th><td>" << acc_c.addr.addr.to_hex() << "</td></tr>";
*this << "<tr><th>account</th><td>" << acc_c.addr.rserialize(true) << "</td></tr>";
if (last_trans_lt > 0) {
*this << "<tr><th>last transaction</th><td>"
<< "<a href=\"" << TransactionLink{acc_c.addr, last_trans_lt, last_trans_hash} << "\">lt=" << last_trans_lt
<< " hash=" << last_trans_hash.to_hex() << "</a></td></tr>\n";
} else {
*this << "<tr><th>last transaction</th><td>no transactions</td></tr>";
}
*this << "</table></div>\n";
*this << "<p><a class=\"btn btn-primary\" href=\"" << prefix_ << "account?account=" << acc_c.addr.rserialize(true)
<< "\">go to current state</a></p>\n";
if (acc_c.root.not_null()) {
*this << RawData<block::gen::Account>{acc_c.root};
} else {
*this << "<div class=\"alert alert-info\">account state is empty</div>";
}
return *this << "</div>";
}
HttpAnswer& HttpAnswer::operator<<(BlockHeaderCell head_c) {
*this << "<div>";
vm::CellSlice cs{vm::NoVm{}, head_c.root};
auto block_id = head_c.block_id;
try {
auto virt_root = vm::MerkleProof::virtualize(head_c.root, 1);
if (virt_root.is_null()) {
abort("invalid merkle proof");
return *this;
}
ton::RootHash vhash{virt_root->get_hash().bits()};
std::vector<ton::BlockIdExt> prev;
ton::BlockIdExt mc_blkid;
bool after_split;
auto res = block::unpack_block_prev_blk_ext(virt_root, block_id, prev, mc_blkid, after_split);
if (res.is_error()) {
abort(PSTRING() << "cannot unpack header for block " << block_id.to_str() << ": " << res);
return *this;
}
block::gen::Block::Record blk;
block::gen::BlockInfo::Record info;
if (!(tlb::unpack_cell(virt_root, blk) && tlb::unpack_cell(blk.info, info))) {
abort(PSTRING() << "cannot unpack header for block " << block_id.to_str());
return *this;
}
bool before_split = info.before_split;
*this << "<div class=\"table-responsive my-3\">\n"
<< "<table class=\"table-sm table-striped\">\n"
<< "<tr><th>block</th><td>" << block_id.id.to_str() << "</td></tr>\n"
<< "<tr><th>roothash</th><td>" << block_id.root_hash.to_hex() << "</td></tr>\n"
<< "<tr><th>filehash</th><td>" << block_id.file_hash.to_hex() << "</td></tr>\n"
<< "<tr><th>time</th><td>" << info.gen_utime << "</td></tr>\n"
<< "<tr><th>lt</th><td>" << info.start_lt << " .. " << info.end_lt
<< "</td></tr>\n"
<< "<tr><th>global_id</th><td>" << blk.global_id << "</td></tr>\n"
<< "<tr><th>version</th><td>" << info.version << "</td></tr>\n"
<< "<tr><th>not_master</th><td>" << info.not_master << "</td></tr>\n"
<< "<tr><th>after_merge</th><td>" << info.after_merge << "</td></tr>\n"
<< "<tr><th>after_split</th><td>" << info.after_split << "</td></tr>\n"
<< "<tr><th>before_split</th><td>" << info.before_split << "</td></tr>\n"
<< "<tr><th>want_merge</th><td>" << info.want_merge << "</td></tr>\n"
<< "<tr><th>want_split</th><td>" << info.want_split << "</td></tr>\n"
<< "<tr><th>validator_list_hash_short</th><td>"
<< info.gen_validator_list_hash_short << "</td></tr>\n"
<< "<tr><th>catchain_seqno</th><td>" << info.gen_catchain_seqno << "</td></tr>\n"
<< "<tr><th>min_ref_mc_seqno</th><td>" << info.min_ref_mc_seqno
<< "</td></tr>\n";
for (auto id : prev) {
*this << "<tr><th>prev block</th><td>" << id << "</td></tr>\n";
}
if (!before_split) {
*this << "<tr><th>next block</th><td>"
<< ton::BlockId{block_id.id.workchain, block_id.id.shard, block_id.id.seqno + 1} << "</td></tr>\n";
} else {
*this << "<tr><th>next block</th><td>"
<< ton::BlockId{block_id.id.workchain, ton::shard_child(block_id.id.shard, true), block_id.id.seqno + 1}
<< "</td></tr>\n";
*this << "<tr><th>next block</th><td>"
<< ton::BlockId{block_id.id.workchain, ton::shard_child(block_id.id.shard, false), block_id.id.seqno + 1}
<< "</td></tr>\n";
}
*this << "<tr><th>masterchain block</th><td>" << mc_blkid << "</td></tr>\n"
<< "</table></div>";
} catch (vm::VmError err) {
abort(PSTRING() << "error processing header : " << err.get_msg());
return *this;
} catch (vm::VmVirtError err) {
abort(PSTRING() << "error processing header : " << err.get_msg());
return *this;
}
return *this << "<p><a class=\"btn btn-primary mr-2\" href=\"" << BlockDownloadLink{block_id} << "\" download=\""
<< block_id.file_hash << ".boc\">download block</a>"
<< "<a class=\"btn btn-primary\" href=\"" << BlockViewLink{block_id} << "\">view block</a>\n"
<< "</p></div>";
}
HttpAnswer& HttpAnswer::operator<<(BlockShardsCell shards_c) {
block::ShardConfig sh_conf;
if (!sh_conf.unpack(vm::load_cell_slice_ref(shards_c.root))) {
abort("cannot extract shard block list from shard configuration");
return *this;
} else {
auto ids = sh_conf.get_shard_hash_ids(true);
auto workchain = ton::masterchainId;
*this << "<div class=\"table-responsive my-3\">\n"
<< "<table class=\"table\">\n<tbody>\n"
<< "<thead>\n"
<< "<tr>\n"
<< "<th scope=\"col\">shard</th>"
<< "<th scope=\"col\">seqno</th>"
<< "<th scope=\"col\">created</th>"
<< "<th scope=\"col\">wantsplit</th>"
<< "<th scope=\"col\">wantmerge</th>"
<< "<th scope=\"col\">beforesplit</th>"
<< "<th scope=\"col\">beforemerge</th>"
<< "</tr>\n"
<< "</thead>\n";
for (auto id : ids) {
auto ref = sh_conf.get_shard_hash(ton::ShardIdFull(id));
if (id.workchain != workchain) {
if (workchain != ton::masterchainId) {
*this << "<tr></tr>\n";
}
workchain = id.workchain;
}
*this << "<tr>";
ton::ShardIdFull shard{id.workchain, id.shard};
if (ref.not_null()) {
*this << "<td>" << shard.to_str() << "</td><td><a href=\"" << HttpAnswer::BlockLink{ref->top_block_id()}
<< "\">" << ref->top_block_id().id.seqno << "</a></td><td>" << ref->created_at() << "</td>"
<< "<td>" << ref->want_split_ << "</td>"
<< "<td>" << ref->want_merge_ << "</td>"
<< "<td>" << ref->before_split_ << "</td>"
<< "<td>" << ref->before_merge_ << "</td>";
} else {
*this << "<td>" << shard.to_str() << "</td>";
}
*this << "</tr>";
}
return *this << "</tbody></table></div>";
}
}
HttpAnswer& HttpAnswer::operator<<(AccountLink account) {
*this << prefix_ << "account?";
if (account.block_id.is_valid()) {
block_id_link(account.block_id);
*this << "&";
}
return *this << "account=" << account.account_id.rserialize(true);
}
HttpAnswer& HttpAnswer::operator<<(MessageLink msg) {
return *this << "#msg" << msg.root->get_hash();
}
HttpAnswer& HttpAnswer::operator<<(TransactionLink trans) {
return *this << prefix_ << "transaction?"
<< "account=" << trans.account_id.rserialize(true) << "&lt=" << trans.lt << "&hash=" << trans.hash;
}
HttpAnswer& HttpAnswer::operator<<(TransactionLinkShort trans) {
*this << prefix_ << "transaction2?";
block_id_link(trans.block_id);
return *this << "&account=" << trans.account_id.rserialize(true) << "&lt=" << trans.lt;
}
HttpAnswer& HttpAnswer::operator<<(BlockLink block) {
*this << prefix_ << "block?";
block_id_link(block.block_id);
return *this;
}
HttpAnswer& HttpAnswer::operator<<(BlockViewLink block) {
*this << prefix_ << "viewblock?";
block_id_link(block.block_id);
return *this;
}
HttpAnswer& HttpAnswer::operator<<(BlockDownloadLink block) {
*this << prefix_ << "download?";
block_id_link(block.block_id);
return *this;
}
HttpAnswer& HttpAnswer::operator<<(TransactionList trans) {
*this << "<div class=\"table-responsive my-3\">\n"
<< "<table class=\"table\">\n<tbody>\n"
<< "<thead>\n"
<< "<tr>\n"
<< "<th scope=\"col\">seq</th>"
<< "<th scope=\"col\">account</th>"
<< "<th scope=\"col\">lt</th>"
<< "<th scope=\"col\">hash</th>"
<< "<th scope=\"col\">link</th>"
<< "</tr>\n"
<< "</thead>\n";
td::uint32 idx = 0;
for (auto& x : trans.vec) {
*this << "<tr><td><a href=\"" << TransactionLink{x.addr, x.lt, x.hash} << "\">" << ++idx << "</a></td>"
<< "<td><a href=\"" << AccountLink{x.addr, trans.block_id} << "\">" << x.addr.rserialize(true) << "</a></td>"
<< "<td>" << x.lt << "</td>"
<< "<td>" << x.hash.to_hex() << "</td>"
<< "<td><a href=\"" << TransactionLink{x.addr, x.lt, x.hash} << "\">view</a></td></tr>";
}
if (trans.vec.size() == trans.req_count_) {
*this << "<tr><td>" << ++idx << "</td>"
<< "<td>more</td>"
<< "<td>more</td>"
<< "<td>more</td></tr>";
}
return *this << "</tbody></table></div>";
}
HttpAnswer& HttpAnswer::operator<<(Error error) {
return *this << "<div class=\"alert alert-danger\">" << error.error.to_string() << "</div>";
}
void HttpAnswer::block_id_link(ton::BlockIdExt block_id) {
*this << "workchain=" << block_id.id.workchain << "&shard=" << ton::shard_to_str(block_id.id.shard)
<< "&seqno=" << block_id.id.seqno << "&roothash=" << block_id.root_hash << "&filehash=" << block_id.file_hash;
}
std::string HttpAnswer::abort(td::Status error) {
if (error_.is_ok()) {
error_ = std::move(error);
}
return header() + "<div class=\"alert alert-danger\">" + error_.to_string() + "</div>" + footer();
}
std::string HttpAnswer::abort(std::string error) {
return abort(td::Status::Error(404, error));
}
std::string HttpAnswer::header() {
sb_->clear();
*this << "<!DOCTYPE html>\n"
<< "<html lang=\"en\"><head><meta charset=\"utf-8\"><title>" << title_ << "</title>\n"
<< "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no\" />\n"
<< "<meta name=\"format-detection\" content=\"telephone=no\" />\n"
<< "<!-- Latest compiled and minified CSS -->\n"
<< "<link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css\">\n"
<< "<!-- jQuery library -->"
<< "<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js\"></script>\n"
<< "<!-- Popper JS -->\n"
<< "<script src=\"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js\"></script>\n"
<< "<!-- Latest compiled JavaScript -->\n"
<< "<script src=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js\"></script>\n"
<< "</head><body>\n"
<< "<div class=\"container-fluid\">\n"
<< "<nav class=\"navbar navbar-expand px-0 mt-1 flex-wrap\">\n"
<< "<ul class=\"navbar-nav ml-1 mr-5 my-1\">\n"
<< "<li class=\"nav-item\"><a class=\"nav-link\" href=\"" << prefix_ << "status\">status</a></li>\n"
<< "<li class=\"nav-item\"><a class=\"nav-link\" href=\"" << prefix_ << "last\">last</a></li>\n"
<< "</ul>";
*this << "<form class=\"my-1 my-lg-0 flex-grow-1\" action=\"" << prefix_ << "account\" method=\"get\">"
<< "<div class=\"input-group ml-auto\" style=\"max-width:540px;\">"
<< "<input class=\"form-control mr-2 rounded\" type=\"search\" placeholder=\"account\" aria-label=\"account\" "
<< "name=\"account\">";
*this << "<div class=\"input-group-append\"><button class=\"btn btn-outline-primary rounded\" type=\"submit\">view</button></div>"
<< "</div></form>"
<< "</nav>\n";
*this << "<p>\n"
<< "<a class=\"btn btn-primary mt-1\" data-toggle=\"collapse\" href=\"#blocksearch\" role=\"button\" "
"aria-expanded=\"false\" aria-controls=\"blocksearch\">\n"
<< "Search block\n"
<< "</a>\n"
<< "<a class=\"btn btn-primary mt-1\" data-toggle=\"collapse\" href=\"#accountsearch\" role=\"button\" "
"aria-expanded=\"false\" aria-controls=\"accountsearch\">\n"
<< "Search account\n"
<< "</a>\n"
<< "<a class=\"btn btn-primary mt-1\" data-toggle=\"collapse\" href=\"#transactionsearch\" role=\"button\" "
"aria-expanded=\"false\" aria-controls=\"transactionsearch\">\n"
<< "Search transaction\n"
<< "</a>\n"
<< "</p>\n";
*this << "<div id=\"searchgroup\">\n"
<< "<div class=\"collapse\" data-parent=\"#searchgroup\" id=\"blocksearch\">\n"
<< "<div class=\"card card-body\">\n"
<< BlockSearch{block_id_} << "</div></div>\n";
*this << "<div class=\"collapse\" data-parent=\"#searchgroup\" id=\"accountsearch\">\n"
<< "<div class=\"card card-body\">\n"
<< AccountSearch{block_id_, account_id_} << "</div></div>\n";
*this << "<div class=\"collapse\" data-parent=\"#searchgroup\" id=\"transactionsearch\">\n"
<< "<div class=\"card card-body\">\n"
<< TransactionSearch{block_id_, account_id_, 0, ton::Bits256::zero()} << "</div></div></div>\n";
return sb_->as_cslice().c_str();
}
std::string HttpAnswer::footer() {
return PSTRING() << "</div></body></html>";
}
std::string HttpAnswer::finish() {
if (error_.is_ok()) {
std::string data = sb_->as_cslice().c_str();
return header() + data + footer();
} else {
return header() + "<div class=\"alert alert-danger\">" + error_.to_string() + "</div>" + footer();
}
}

View file

@ -0,0 +1,222 @@
/*
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
*/
#pragma once
#include "ton/ton-types.h"
#include "vm/boc.h"
#include "vm/cellops.h"
#include "td/utils/Random.h"
#include "block/block.h"
class HttpAnswer {
public:
struct MessageCell {
td::Ref<vm::Cell> root;
};
struct AddressCell {
td::Ref<vm::CellSlice> root;
};
struct TransactionCell {
block::StdAddress addr;
ton::BlockIdExt block_id;
td::Ref<vm::Cell> root;
};
struct AccountCell {
block::StdAddress addr;
ton::BlockIdExt block_id;
td::Ref<vm::Cell> root;
std::vector<td::Ref<vm::Cell>> q_roots;
};
struct BlockHeaderCell {
ton::BlockIdExt block_id;
td::Ref<vm::Cell> root;
};
struct BlockShardsCell {
ton::BlockIdExt block_id;
td::Ref<vm::Cell> root;
};
struct AccountLink {
block::StdAddress account_id;
ton::BlockIdExt block_id;
};
struct MessageLink {
td::Ref<vm::Cell> root;
};
struct TransactionLink {
block::StdAddress account_id;
ton::LogicalTime lt;
ton::Bits256 hash;
};
struct TransactionLinkShort {
ton::BlockIdExt block_id;
block::StdAddress account_id;
ton::LogicalTime lt;
};
struct BlockLink {
ton::BlockIdExt block_id;
};
struct BlockViewLink {
ton::BlockIdExt block_id;
};
struct BlockDownloadLink {
ton::BlockIdExt block_id;
};
struct BlockSearch {
ton::BlockIdExt block_id;
};
struct AccountSearch {
ton::BlockIdExt block_id;
block::StdAddress addr;
};
struct TransactionSearch {
ton::BlockIdExt block_id;
block::StdAddress addr;
ton::LogicalTime lt;
ton::Bits256 hash;
};
struct TransactionList {
struct TransactionDescr {
TransactionDescr(block::StdAddress addr, ton::LogicalTime lt, ton::Bits256 hash)
: addr(addr), lt(lt), hash(hash) {
}
block::StdAddress addr;
ton::LogicalTime lt;
ton::Bits256 hash;
};
ton::BlockIdExt block_id;
std::vector<TransactionDescr> vec;
td::uint32 req_count_;
};
struct CodeBlock {
std::string data;
};
struct Error {
td::Status error;
};
template <class T>
struct RawData {
td::Ref<vm::Cell> root;
T x;
template <typename... Args>
RawData(td::Ref<vm::Cell> root, Args &&... args) : root(std::move(root)), x(std::forward<Args>(args)...) {
}
};
public:
HttpAnswer(std::string title, std::string prefix) : title_(title), prefix_(prefix) {
buf_ = td::BufferSlice{1 << 28};
sb_ = std::make_unique<td::StringBuilder>(buf_.as_slice());
}
void set_title(std::string title) {
title_ = title;
}
void set_block_id(ton::BlockIdExt block_id) {
block_id_ = block_id;
workchain_id_ = block_id_.id.workchain;
}
void set_account_id(block::StdAddress addr) {
account_id_ = addr;
}
void set_workchain(ton::WorkchainId workchain_id) {
workchain_id_ = workchain_id;
}
std::string abort(td::Status error);
std::string abort(std::string error);
std::string finish();
std::string header();
std::string footer();
template <typename T>
HttpAnswer &operator<<(T x) {
sb() << x;
return *this;
}
td::StringBuilder &sb() {
return *sb_;
}
HttpAnswer &operator<<(td::Bits256 x) {
sb() << x.to_hex();
return *this;
}
HttpAnswer &operator<<(td::BitString x) {
sb() << x.to_hex();
return *this;
}
HttpAnswer &operator<<(AddressCell addr);
HttpAnswer &operator<<(MessageCell msg);
HttpAnswer &operator<<(ton::BlockIdExt block_id);
HttpAnswer &operator<<(ton::BlockId block_id);
HttpAnswer &operator<<(TransactionCell trans);
HttpAnswer &operator<<(AccountCell trans);
HttpAnswer &operator<<(BlockHeaderCell head);
HttpAnswer &operator<<(BlockShardsCell shards);
HttpAnswer &operator<<(BlockSearch head);
HttpAnswer &operator<<(AccountSearch head);
HttpAnswer &operator<<(TransactionSearch head);
HttpAnswer &operator<<(AccountLink account);
HttpAnswer &operator<<(MessageLink msg);
HttpAnswer &operator<<(TransactionLink trans);
HttpAnswer &operator<<(TransactionLinkShort trans);
HttpAnswer &operator<<(BlockLink block);
HttpAnswer &operator<<(BlockViewLink block);
HttpAnswer &operator<<(BlockDownloadLink block);
HttpAnswer &operator<<(Error error);
HttpAnswer &operator<<(TransactionList trans);
HttpAnswer &operator<<(CodeBlock block) {
return *this << "<pre><code>" << block.data << "</code></pre>";
}
template <class T>
HttpAnswer &operator<<(RawData<T> data) {
std::ostringstream outp;
data.x.print_ref(outp, data.root);
vm::load_cell_slice(data.root).print_rec(outp);
return *this << CodeBlock{outp.str()};
}
private:
void block_id_link(ton::BlockIdExt block_id);
std::string title_;
ton::BlockIdExt block_id_;
ton::WorkchainId workchain_id_ = ton::workchainInvalid;
block::StdAddress account_id_;
std::string prefix_;
td::Status error_;
std::unique_ptr<td::StringBuilder> sb_;
td::BufferSlice buf_;
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,274 @@
/*
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
*/
#pragma once
#include "td/actor/actor.h"
#include "ton/ton-types.h"
#include "block/block.h"
#include "blockchain-explorer.hpp"
#include <map>
#include <microhttpd.h>
td::Result<ton::BlockIdExt> parse_block_id(std::map<std::string, std::string> &opts, bool allow_empty = false);
td::Result<block::StdAddress> parse_account_addr(std::map<std::string, std::string> &opts);
class HttpAnswer;
class HttpQueryCommon : public td::actor::Actor {
public:
HttpQueryCommon(std::string prefix, td::Promise<MHD_Response *> promise)
: prefix_(std::move(prefix)), promise_(std::move(promise)) {
}
void start_up() override {
if (error_.is_error()) {
abort_query(std::move(error_));
return;
}
start_up_query();
}
virtual void start_up_query() {
UNREACHABLE();
}
virtual void abort_query(td::Status error);
void create_header(HttpAnswer &ans) {
}
protected:
td::Status error_;
std::string prefix_;
td::Promise<MHD_Response *> promise_;
};
class HttpQueryBlockData : public HttpQueryCommon {
public:
HttpQueryBlockData(ton::BlockIdExt block_id, std::string prefix, td::Promise<MHD_Response *> promise);
HttpQueryBlockData(std::map<std::string, std::string> opts, std::string prefix, td::Promise<MHD_Response *> promise);
void abort_query(td::Status error) override;
void finish_query();
void start_up() override;
void got_block_data(td::BufferSlice result);
private:
ton::BlockIdExt block_id_;
td::BufferSlice data_;
};
class HttpQueryBlockView : public HttpQueryCommon {
public:
HttpQueryBlockView(ton::BlockIdExt block_id, std::string prefix, td::Promise<MHD_Response *> promise);
HttpQueryBlockView(std::map<std::string, std::string> opts, std::string prefix, td::Promise<MHD_Response *> promise);
void finish_query();
void start_up_query() override;
void got_block_data(td::BufferSlice result);
private:
ton::BlockIdExt block_id_;
td::BufferSlice data_;
};
class HttpQueryBlockInfo : public HttpQueryCommon {
public:
HttpQueryBlockInfo(ton::BlockIdExt block_id, std::string prefix, td::Promise<MHD_Response *> promise);
HttpQueryBlockInfo(std::map<std::string, std::string> opts, std::string prefix, td::Promise<MHD_Response *> promise);
void finish_query();
void start_up_query() override;
void got_block_header(td::BufferSlice result);
void got_shard_info(td::BufferSlice result);
void got_transactions(td::BufferSlice result);
void failed_to_get_shard_info(td::Status error);
private:
ton::BlockIdExt block_id_;
td::int32 pending_queries_ = 0;
td::BufferSlice data_;
td::BufferSlice shard_data_;
td::Status shard_data_error_;
struct TransactionDescr {
TransactionDescr(block::StdAddress addr, ton::LogicalTime lt, ton::Bits256 hash) : addr(addr), lt(lt), hash(hash) {
}
block::StdAddress addr;
ton::LogicalTime lt;
ton::Bits256 hash;
};
std::vector<TransactionDescr> transactions_;
td::uint32 trans_req_count_;
};
class HttpQueryBlockSearch : public HttpQueryCommon {
public:
HttpQueryBlockSearch(ton::WorkchainId workchain, ton::AccountIdPrefix account, ton::BlockSeqno seqno,
std::string prefix, td::Promise<MHD_Response *> promise);
HttpQueryBlockSearch(ton::WorkchainId workchain, ton::AccountIdPrefix account, ton::LogicalTime lt,
std::string prefix, td::Promise<MHD_Response *> promise);
HttpQueryBlockSearch(ton::WorkchainId workchain, ton::AccountIdPrefix account, bool dummy, ton::UnixTime utime,
std::string prefix, td::Promise<MHD_Response *> promise);
HttpQueryBlockSearch(std::map<std::string, std::string> opts, std::string prefix,
td::Promise<MHD_Response *> promise);
void finish_query();
void start_up_query() override;
void got_block_header(td::BufferSlice result);
void got_shard_info(td::BufferSlice result);
void got_transactions(td::BufferSlice result);
void failed_to_get_shard_info(td::Status error);
private:
ton::AccountIdPrefixFull account_prefix_;
td::uint32 mode_ = 0;
ton::BlockSeqno seqno_ = 0;
ton::LogicalTime lt_ = 0;
ton::UnixTime utime_ = 0;
ton::BlockIdExt block_id_;
td::BufferSlice data_;
td::BufferSlice shard_data_;
td::Status shard_data_error_;
td::uint32 pending_queries_ = 0;
struct TransactionDescr {
TransactionDescr(block::StdAddress addr, ton::LogicalTime lt, ton::Bits256 hash) : addr(addr), lt(lt), hash(hash) {
}
block::StdAddress addr;
ton::LogicalTime lt;
ton::Bits256 hash;
};
std::vector<TransactionDescr> transactions_;
td::uint32 trans_req_count_;
};
class HttpQueryViewAccount : public HttpQueryCommon {
public:
HttpQueryViewAccount(ton::BlockIdExt block_id, block::StdAddress addr, std::string prefix,
td::Promise<MHD_Response *> promise);
HttpQueryViewAccount(std::map<std::string, std::string> opts, std::string prefix,
td::Promise<MHD_Response *> promise);
void finish_query();
void start_up_query() override;
void got_account(td::BufferSlice result);
private:
ton::BlockIdExt block_id_;
block::StdAddress addr_;
td::BufferSlice data_;
td::BufferSlice proof_;
ton::BlockIdExt res_block_id_;
};
class HttpQueryViewTransaction : public HttpQueryCommon {
public:
HttpQueryViewTransaction(block::StdAddress addr, ton::LogicalTime lt, ton::Bits256 hash, std::string prefix,
td::Promise<MHD_Response *> promise);
HttpQueryViewTransaction(std::map<std::string, std::string> opts, std::string prefix,
td::Promise<MHD_Response *> promise);
void finish_query();
void start_up_query() override;
void got_transaction(td::BufferSlice result);
private:
block::StdAddress addr_;
ton::LogicalTime lt_;
ton::Bits256 hash_;
td::BufferSlice data_;
ton::BlockIdExt res_block_id_;
};
class HttpQueryViewTransaction2 : public HttpQueryCommon {
public:
HttpQueryViewTransaction2(ton::BlockIdExt block_id, block::StdAddress addr, ton::LogicalTime lt, std::string prefix,
td::Promise<MHD_Response *> promise);
HttpQueryViewTransaction2(std::map<std::string, std::string> opts, std::string prefix,
td::Promise<MHD_Response *> promise);
void finish_query();
void start_up_query() override;
void got_transaction(td::BufferSlice result);
private:
ton::BlockIdExt block_id_;
block::StdAddress addr_;
ton::LogicalTime lt_;
ton::Bits256 hash_;
td::BufferSlice data_;
};
class HttpQueryViewLastBlock : public HttpQueryCommon {
public:
HttpQueryViewLastBlock(std::string prefix, td::Promise<MHD_Response *> promise);
HttpQueryViewLastBlock(std::map<std::string, std::string> opts, std::string prefix,
td::Promise<MHD_Response *> promise);
void finish_query();
void start_up() override;
void got_result(td::BufferSlice result);
private:
ton::BlockIdExt res_block_id_;
};
class HttpQueryStatus : public HttpQueryCommon {
public:
HttpQueryStatus(std::string prefix, td::Promise<MHD_Response *> promise);
HttpQueryStatus(std::map<std::string, std::string> opts, std::string prefix, td::Promise<MHD_Response *> promise);
void finish_query();
void start_up() override;
void got_results(CoreActorInterface::RemoteNodeStatusList results);
private:
CoreActorInterface::RemoteNodeStatusList results_;
};

View file

@ -0,0 +1,582 @@
/*
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
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.
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "adnl/adnl-ext-client.h"
#include "adnl/utils.hpp"
#include "auto/tl/ton_api_json.h"
#include "td/utils/OptionsParser.h"
#include "td/utils/Time.h"
#include "td/utils/filesystem.h"
#include "td/utils/format.h"
#include "td/utils/Random.h"
#include "td/utils/crypto.h"
#include "td/utils/port/signals.h"
#include "td/utils/port/user.h"
#include "td/utils/port/FileFd.h"
#include "ton/ton-tl.hpp"
#include "block/block-db.h"
#include "block/block.h"
#include "block/block-auto.h"
#include "vm/boc.h"
#include "vm/cellops.h"
#include "vm/cells/MerkleProof.h"
#include "block/mc-config.h"
#include "blockchain-explorer.hpp"
#include "blockchain-explorer-http.hpp"
#include "blockchain-explorer-query.hpp"
#include "auto/tl/lite_api.h"
#include "ton/lite-tl.hpp"
#include "tl-utils/lite-utils.hpp"
#include <microhttpd.h>
#if TD_DARWIN || TD_LINUX
#include <unistd.h>
#include <fcntl.h>
#endif
#include <iostream>
#include <sstream>
int verbosity;
td::actor::Scheduler* scheduler_ptr;
static std::string urldecode(td::Slice from, bool decode_plus_sign_as_space) {
size_t to_i = 0;
td::BufferSlice x{from.size()};
auto to = x.as_slice();
for (size_t from_i = 0, n = from.size(); from_i < n; from_i++) {
if (from[from_i] == '%' && from_i + 2 < n) {
int high = td::hex_to_int(from[from_i + 1]);
int low = td::hex_to_int(from[from_i + 2]);
if (high < 16 && low < 16) {
to[to_i++] = static_cast<char>(high * 16 + low);
from_i += 2;
continue;
}
}
to[to_i++] = decode_plus_sign_as_space && from[from_i] == '+' ? ' ' : from[from_i];
}
return to.truncate(to_i).str();
}
class HttpQueryRunner {
public:
HttpQueryRunner(std::function<void(td::Promise<MHD_Response*>)> func) {
auto P = td::PromiseCreator::lambda([Self = this](td::Result<MHD_Response*> R) {
if (R.is_ok()) {
Self->finish(R.move_as_ok());
} else {
Self->finish(nullptr);
}
});
mutex_.lock();
scheduler_ptr->run_in_context_external([&]() { func(std::move(P)); });
}
void finish(MHD_Response* response) {
response_ = response;
mutex_.unlock();
}
MHD_Response* wait() {
mutex_.lock();
mutex_.unlock();
return response_;
}
private:
std::function<void(td::Promise<MHD_Response*>)> func_;
MHD_Response* response_;
std::mutex mutex_;
};
class CoreActor : public CoreActorInterface {
private:
std::string global_config_ = "ton-global.config";
std::vector<td::actor::ActorOwn<ton::adnl::AdnlExtClient>> clients_;
td::uint32 http_port_ = 80;
MHD_Daemon* daemon_ = nullptr;
td::IPAddress remote_addr_;
ton::PublicKey remote_public_key_;
bool hide_ips_ = false;
std::unique_ptr<ton::adnl::AdnlExtClient::Callback> make_callback(td::uint32 idx) {
class Callback : public ton::adnl::AdnlExtClient::Callback {
public:
void on_ready() override {
td::actor::send_closure(id_, &CoreActor::conn_ready, idx_);
}
void on_stop_ready() override {
td::actor::send_closure(id_, &CoreActor::conn_closed, idx_);
}
Callback(td::actor::ActorId<CoreActor> id, td::uint32 idx) : id_(std::move(id)), idx_(idx) {
}
private:
td::actor::ActorId<CoreActor> id_;
td::uint32 idx_;
};
return std::make_unique<Callback>(actor_id(this), idx);
}
std::shared_ptr<RemoteNodeStatus> new_result_;
td::int32 attempt_ = 0;
td::int32 waiting_ = 0;
std::vector<bool> ready_;
void run_queries();
void got_result(td::uint32 idx, td::int32 attempt, td::Result<td::BufferSlice> data);
void send_query(td::uint32 idx);
void add_result() {
if (new_result_) {
auto ts = static_cast<td::int32>(new_result_->ts_.at_unix());
results_.emplace(ts, std::move(new_result_));
}
}
void alarm() override {
auto t = static_cast<td::int32>(td::Clocks::system() / 60);
if (t <= attempt_) {
alarm_timestamp() = td::Timestamp::at_unix((attempt_ + 1) * 60);
return;
}
if (waiting_ > 0 && new_result_) {
add_result();
}
attempt_ = t;
run_queries();
alarm_timestamp() = td::Timestamp::at_unix((attempt_ + 1) * 60);
}
public:
std::mutex queue_mutex_;
std::mutex res_mutex_;
std::map<td::int32, std::shared_ptr<RemoteNodeStatus>> results_;
std::vector<td::IPAddress> addrs_;
static CoreActor* instance_;
td::actor::ActorId<CoreActor> self_id_;
void conn_ready(td::uint32 idx) {
ready_.at(idx) = true;
}
void conn_closed(td::uint32 idx) {
ready_.at(idx) = false;
}
void set_global_config(std::string str) {
global_config_ = str;
}
void set_http_port(td::uint32 port) {
http_port_ = port;
}
void set_remote_addr(td::IPAddress addr) {
remote_addr_ = addr;
}
void set_remote_public_key(td::BufferSlice file_name) {
auto R = [&]() -> td::Result<ton::PublicKey> {
TRY_RESULT_PREFIX(conf_data, td::read_file(file_name.as_slice().str()), "failed to read: ");
return ton::PublicKey::import(conf_data.as_slice());
}();
if (R.is_error()) {
LOG(FATAL) << "bad server public key: " << R.move_as_error();
}
remote_public_key_ = R.move_as_ok();
}
void set_hide_ips(bool value) {
hide_ips_ = value;
}
void send_lite_query(td::uint32 idx, td::BufferSlice query, td::Promise<td::BufferSlice> promise);
void send_lite_query(td::BufferSlice data, td::Promise<td::BufferSlice> promise) override {
return send_lite_query(0, std::move(data), std::move(promise));
}
void get_last_result(td::Promise<std::shared_ptr<RemoteNodeStatus>> promise) override {
}
void get_results(td::uint32 max, td::Promise<RemoteNodeStatusList> promise) override {
RemoteNodeStatusList r;
r.ips = hide_ips_ ? std::vector<td::IPAddress>{addrs_.size()} : addrs_;
auto it = results_.rbegin();
while (it != results_.rend() && r.results.size() < max) {
r.results.push_back(it->second);
it++;
}
promise.set_value(std::move(r));
}
void start_up() override {
instance_ = this;
auto t = td::Clocks::system();
attempt_ = static_cast<td::int32>(t / 60);
auto next_t = (attempt_ + 1) * 60;
alarm_timestamp() = td::Timestamp::at_unix(next_t);
self_id_ = actor_id(this);
}
void tear_down() override {
if (daemon_) {
MHD_stop_daemon(daemon_);
daemon_ = nullptr;
}
}
CoreActor() {
}
static int get_arg_iterate(void* cls, enum MHD_ValueKind kind, const char* key, const char* value) {
auto X = static_cast<std::map<std::string, std::string>*>(cls);
if (key && value && std::strlen(key) > 0 && std::strlen(value) > 0) {
X->emplace(key, urldecode(td::Slice{value}, false));
}
return MHD_YES;
}
static int process_http_request(void* cls, struct MHD_Connection* connection, const char* url, const char* method,
const char* version, const char* upload_data, size_t* upload_data_size, void** ptr) {
static int dummy;
struct MHD_Response* response = nullptr;
int ret;
if (0 != std::strcmp(method, "GET"))
return MHD_NO; /* unexpected method */
if (&dummy != *ptr) {
/* The first time only the headers are valid,
do not respond in the first round... */
*ptr = &dummy;
return MHD_YES;
}
if (0 != *upload_data_size)
return MHD_NO; /* upload data in a GET!? */
std::string url_s = url;
*ptr = nullptr; /* clear context pointer */
auto pos = url_s.rfind('/');
std::string prefix;
std::string command;
if (pos == std::string::npos) {
prefix = "";
command = url_s;
} else {
prefix = url_s.substr(0, pos + 1);
command = url_s.substr(pos + 1);
}
std::map<std::string, std::string> opts;
MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, get_arg_iterate, static_cast<void*>(&opts));
if (command == "status") {
HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
td::actor::create_actor<HttpQueryStatus>("blockinfo", opts, prefix, std::move(promise)).release();
}};
response = g.wait();
} else if (command == "block") {
HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
td::actor::create_actor<HttpQueryBlockInfo>("blockinfo", opts, prefix, std::move(promise)).release();
}};
response = g.wait();
} else if (command == "search") {
if (opts.count("roothash") + opts.count("filehash") > 0) {
HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
td::actor::create_actor<HttpQueryBlockInfo>("blockinfo", opts, prefix, std::move(promise)).release();
}};
response = g.wait();
} else {
HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
td::actor::create_actor<HttpQueryBlockSearch>("blocksearch", opts, prefix, std::move(promise)).release();
}};
response = g.wait();
}
} else if (command == "last") {
HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
td::actor::create_actor<HttpQueryViewLastBlock>("", opts, prefix, std::move(promise)).release();
}};
response = g.wait();
} else if (command == "download") {
HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
td::actor::create_actor<HttpQueryBlockData>("downloadblock", opts, prefix, std::move(promise)).release();
}};
response = g.wait();
} else if (command == "viewblock") {
HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
td::actor::create_actor<HttpQueryBlockView>("viewblock", opts, prefix, std::move(promise)).release();
}};
response = g.wait();
} else if (command == "account") {
HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
td::actor::create_actor<HttpQueryViewAccount>("viewaccount", opts, prefix, std::move(promise)).release();
}};
response = g.wait();
} else if (command == "transaction") {
HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
td::actor::create_actor<HttpQueryViewTransaction>("viewtransaction", opts, prefix, std::move(promise))
.release();
}};
response = g.wait();
} else if (command == "transaction2") {
HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
td::actor::create_actor<HttpQueryViewTransaction2>("viewtransaction2", opts, prefix, std::move(promise))
.release();
}};
response = g.wait();
} else {
ret = MHD_NO;
}
if (response) {
ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
MHD_destroy_response(response);
} else {
ret = MHD_NO;
}
return ret;
}
void run() {
if (remote_public_key_.empty()) {
auto G = td::read_file(global_config_).move_as_ok();
auto gc_j = td::json_decode(G.as_slice()).move_as_ok();
ton::ton_api::liteclient_config_global gc;
ton::ton_api::from_json(gc, gc_j.get_object()).ensure();
CHECK(gc.liteservers_.size() > 0);
td::uint32 size = static_cast<td::uint32>(gc.liteservers_.size());
ready_.resize(size, false);
for (td::uint32 i = 0; i < size; i++) {
auto& cli = gc.liteservers_[i];
td::IPAddress addr;
addr.init_host_port(td::IPAddress::ipv4_to_str(cli->ip_), cli->port_).ensure();
addrs_.push_back(addr);
clients_.emplace_back(ton::adnl::AdnlExtClient::create(ton::adnl::AdnlNodeIdFull::create(cli->id_).move_as_ok(),
addr, make_callback(i)));
}
} else {
if (!remote_addr_.is_valid()) {
LOG(FATAL) << "remote addr not set";
}
ready_.resize(1, false);
addrs_.push_back(remote_addr_);
clients_.emplace_back(ton::adnl::AdnlExtClient::create(ton::adnl::AdnlNodeIdFull{remote_public_key_},
remote_addr_, make_callback(0)));
}
daemon_ = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, static_cast<td::uint16>(http_port_), nullptr, nullptr,
&process_http_request, nullptr, MHD_OPTION_END);
CHECK(daemon_ != nullptr);
}
};
void CoreActor::got_result(td::uint32 idx, td::int32 attempt, td::Result<td::BufferSlice> R) {
if (attempt != attempt_) {
return;
}
if (R.is_error()) {
waiting_--;
if (waiting_ == 0) {
add_result();
}
return;
}
auto data = R.move_as_ok();
{
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_error>(data.clone(), true);
if (F.is_ok()) {
auto f = F.move_as_ok();
auto err = td::Status::Error(f->code_, f->message_);
waiting_--;
if (waiting_ == 0) {
add_result();
}
return;
}
}
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_masterchainInfo>(std::move(data), true);
if (F.is_error()) {
waiting_--;
if (waiting_ == 0) {
add_result();
}
return;
}
auto f = F.move_as_ok();
new_result_->values_[idx] = ton::create_block_id(f->last_);
waiting_--;
CHECK(waiting_ >= 0);
if (waiting_ == 0) {
add_result();
}
}
void CoreActor::send_query(td::uint32 idx) {
if (!ready_[idx]) {
return;
}
waiting_++;
auto query = ton::create_tl_object<ton::lite_api::liteServer_getMasterchainInfo>();
auto q = ton::create_tl_object<ton::lite_api::liteServer_query>(serialize_tl_object(query, true));
auto P =
td::PromiseCreator::lambda([SelfId = actor_id(this), idx, attempt = attempt_](td::Result<td::BufferSlice> R) {
td::actor::send_closure(SelfId, &CoreActor::got_result, idx, attempt, std::move(R));
});
td::actor::send_closure(clients_[idx], &ton::adnl::AdnlExtClient::send_query, "query", serialize_tl_object(q, true),
td::Timestamp::in(10.0), std::move(P));
}
void CoreActor::run_queries() {
waiting_ = 0;
new_result_ = std::make_shared<RemoteNodeStatus>(ready_.size(), td::Timestamp::at_unix(attempt_ * 60));
for (td::uint32 i = 0; i < ready_.size(); i++) {
send_query(i);
}
CHECK(waiting_ >= 0);
if (waiting_ == 0) {
add_result();
}
}
void CoreActor::send_lite_query(td::uint32 idx, td::BufferSlice query, td::Promise<td::BufferSlice> promise) {
if (!ready_[idx]) {
promise.set_error(td::Status::Error(ton::ErrorCode::notready, "ext conn not ready"));
return;
}
auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result<td::BufferSlice> R) mutable {
if (R.is_error()) {
promise.set_error(R.move_as_error());
return;
}
auto B = R.move_as_ok();
{
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_error>(B.clone(), true);
if (F.is_ok()) {
auto f = F.move_as_ok();
promise.set_error(td::Status::Error(f->code_, f->message_));
return;
}
}
promise.set_value(std::move(B));
});
auto q = ton::create_tl_object<ton::lite_api::liteServer_query>(std::move(query));
td::actor::send_closure(clients_[idx], &ton::adnl::AdnlExtClient::send_query, "query", serialize_tl_object(q, true),
td::Timestamp::in(10.0), std::move(P));
}
td::actor::ActorId<CoreActorInterface> CoreActorInterface::instance_actor_id() {
auto instance = CoreActor::instance_;
CHECK(instance);
return instance->self_id_;
}
CoreActor* CoreActor::instance_ = nullptr;
int main(int argc, char* argv[]) {
SET_VERBOSITY_LEVEL(verbosity_INFO);
td::set_default_failure_signal_handler().ensure();
td::actor::ActorOwn<CoreActor> x;
td::OptionsParser p;
p.set_description("TON Blockchain explorer");
p.add_option('h', "help", "prints_help", [&]() {
char b[10240];
td::StringBuilder sb(td::MutableSlice{b, 10000});
sb << p;
std::cout << sb.as_cslice().c_str();
std::exit(2);
return td::Status::OK();
});
p.add_option('I', "hide-ips", "hides ips from status", [&]() {
td::actor::send_closure(x, &CoreActor::set_hide_ips, true);
return td::Status::OK();
});
p.add_option('u', "user", "change user", [&](td::Slice user) { return td::change_user(user); });
p.add_option('C', "global-config", "file to read global config", [&](td::Slice fname) {
td::actor::send_closure(x, &CoreActor::set_global_config, fname.str());
return td::Status::OK();
});
p.add_option('a', "addr", "connect to ip:port", [&](td::Slice arg) {
td::IPAddress addr;
TRY_STATUS(addr.init_host_port(arg.str()));
td::actor::send_closure(x, &CoreActor::set_remote_addr, addr);
return td::Status::OK();
});
p.add_option('p', "pub", "remote public key", [&](td::Slice arg) {
td::actor::send_closure(x, &CoreActor::set_remote_public_key, td::BufferSlice{arg});
return td::Status::OK();
});
p.add_option('v', "verbosity", "set verbosity level", [&](td::Slice arg) {
verbosity = td::to_integer<int>(arg);
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + verbosity);
return (verbosity >= 0 && verbosity <= 9) ? td::Status::OK() : td::Status::Error("verbosity must be 0..9");
});
p.add_option('d', "daemonize", "set SIGHUP", [&]() {
td::set_signal_handler(td::SignalType::HangUp, [](int sig) {
#if TD_DARWIN || TD_LINUX
close(0);
setsid();
#endif
}).ensure();
return td::Status::OK();
});
p.add_option('H', "http-port", "listen on http port", [&](td::Slice arg) {
td::actor::send_closure(x, &CoreActor::set_http_port, td::to_integer<td::uint32>(arg));
return td::Status::OK();
});
#if TD_DARWIN || TD_LINUX
p.add_option('l', "logname", "log to file", [&](td::Slice fname) {
auto FileLog = td::FileFd::open(td::CSlice(fname.str().c_str()),
td::FileFd::Flags::Create | td::FileFd::Flags::Append | td::FileFd::Flags::Write)
.move_as_ok();
dup2(FileLog.get_native_fd().fd(), 1);
dup2(FileLog.get_native_fd().fd(), 2);
return td::Status::OK();
});
#endif
td::actor::Scheduler scheduler({2});
scheduler_ptr = &scheduler;
scheduler.run_in_context([&] { x = td::actor::create_actor<CoreActor>("testnode"); });
scheduler.run_in_context([&] { p.run(argc, argv).ensure(); });
scheduler.run_in_context([&] {
td::actor::send_closure(x, &CoreActor::run);
x.release();
});
scheduler.run();
return 0;
}

View file

@ -0,0 +1,56 @@
/*
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
*/
#pragma once
#include "td/actor/actor.h"
#include "td/utils/buffer.h"
#include "ton/ton-types.h"
#include "td/utils/port/IPAddress.h"
class CoreActorInterface : public td::actor::Actor {
public:
struct RemoteNodeStatus {
std::vector<ton::BlockIdExt> values_;
td::Timestamp ts_;
RemoteNodeStatus(size_t size, td::Timestamp ts) : ts_(ts) {
values_.resize(size);
}
};
struct RemoteNodeStatusList {
std::vector<td::IPAddress> ips;
std::vector<std::shared_ptr<RemoteNodeStatus>> results;
};
virtual ~CoreActorInterface() = default;
virtual void send_lite_query(td::BufferSlice data, td::Promise<td::BufferSlice> promise) = 0;
virtual void get_last_result(td::Promise<std::shared_ptr<RemoteNodeStatus>> promise) = 0;
virtual void get_results(td::uint32 max, td::Promise<RemoteNodeStatusList> promise) = 0;
static td::actor::ActorId<CoreActorInterface> instance_actor_id();
};

39
catchain/CMakeLists.txt Normal file
View file

@ -0,0 +1,39 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
set(CATCHAIN_SOURCE
catchain-received-block.cpp
#catchain-receiver-fork.cpp
catchain-receiver-source.cpp
catchain-receiver.cpp
catchain-block.cpp
catchain.cpp
catchain-block.hpp
catchain-received-block.h
catchain-received-block.hpp
#catchain-receiver-fork.h
#catchain-receiver-fork.hpp
catchain-receiver-interface.h
catchain-receiver-source.h
catchain-receiver-source.hpp
catchain-receiver.h
catchain-receiver.hpp
catchain-types.h
catchain.h
catchain.hpp
)
add_library(catchain STATIC ${CATCHAIN_SOURCE})
target_include_directories(overlay PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/..
${OPENSSL_INCLUDE_DIR}
)
target_link_libraries(catchain PRIVATE tdutils tdactor adnl tl_api dht tdfec
overlay)

View file

@ -0,0 +1,59 @@
/*
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 "catchain-block.hpp"
namespace ton {
namespace catchain {
std::unique_ptr<CatChainBlock> CatChainBlock::create(td::uint32 src, td::uint32 fork, PublicKeyHash src_hash,
CatChainBlockHeight height, CatChainBlockHash hash,
td::SharedSlice payload, CatChainBlock *prev,
std::vector<CatChainBlock *> deps,
std::vector<CatChainBlockHeight> vt) {
return std::make_unique<CatChainBlockImpl>(src, fork, src_hash, height, hash, std::move(payload), prev,
std::move(deps), std::move(vt));
}
CatChainBlockImpl::CatChainBlockImpl(td::uint32 src, td::uint32 fork, PublicKeyHash src_hash,
CatChainBlockHeight height, CatChainBlockHash hash, td::SharedSlice payload,
CatChainBlock *prev, std::vector<CatChainBlock *> deps,
std::vector<CatChainBlockHeight> vt)
: src_(src)
, fork_(fork)
, src_hash_(src_hash)
, height_(height)
, hash_(hash)
, payload_(std::move(payload))
, prev_(prev)
, deps_(std::move(deps))
, vt_(std::move(vt)) {
}
bool CatChainBlockImpl::is_descendant_of(CatChainBlock *block) {
auto fork = block->fork();
if (fork >= vt_.size()) {
return false;
}
return block->height() <= vt_[fork];
}
} // namespace catchain
} // namespace ton

113
catchain/catchain-block.hpp Normal file
View file

@ -0,0 +1,113 @@
/*
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 "catchain.h"
namespace ton {
namespace catchain {
class CatChainBlockImpl : public CatChainBlock {
private:
std::unique_ptr<CatChainBlock::Extra> extra_;
td::uint32 src_;
td::uint32 fork_;
PublicKeyHash src_hash_;
CatChainBlockHeight height_;
CatChainBlockHash hash_;
td::SharedSlice payload_;
CatChainBlock *prev_;
std::vector<CatChainBlock *> deps_;
std::vector<CatChainBlockHeight> vt_;
bool preprocess_sent_ = false;
bool is_processed_ = false;
public:
td::SharedSlice &payload() override {
return payload_;
}
const td::SharedSlice &payload() const override {
return payload_;
}
CatChainBlock::Extra *extra() const override {
return extra_.get();
}
std::unique_ptr<Extra> move_extra() override {
return std::move(extra_);
}
void set_extra(std::unique_ptr<Extra> extra) override {
extra_ = std::move(extra);
}
td::uint32 source() const override {
return src_;
}
td::uint32 fork() const override {
return fork_;
}
PublicKeyHash source_hash() const override {
return src_hash_;
}
CatChainBlockHash hash() const override {
return hash_;
}
CatChainBlockHeight height() const override {
return height_;
}
CatChainBlock *prev() override {
return prev_;
}
const CatChainBlock *prev() const override {
return prev_;
}
const std::vector<CatChainBlock *> &deps() const override {
return deps_;
}
const std::vector<CatChainBlockHeight> &vt() const override {
return vt_;
}
bool preprocess_is_sent() const override {
return preprocess_sent_;
}
void preprocess_sent() override {
preprocess_sent_ = true;
}
bool is_processed() const override {
return is_processed_;
}
void set_processed() override {
is_processed_ = true;
}
bool is_descendant_of(CatChainBlock *block) override;
CatChainBlockImpl(td::uint32 src, td::uint32 fork, PublicKeyHash src_hash, CatChainBlockHeight height,
CatChainBlockHash hash, td::SharedSlice payload, CatChainBlock *prev,
std::vector<CatChainBlock *> deps, std::vector<CatChainBlockHeight> vt);
};
} // namespace catchain
} // namespace ton

View file

@ -0,0 +1,523 @@
/*
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 <set>
#include "catchain-received-block.hpp"
#include "catchain-receiver-source.h"
#include "auto/tl/ton_api.hpp"
namespace ton {
namespace catchain {
void CatChainReceivedBlockImpl::initialize(tl_object_ptr<ton_api::catchain_block> block, td::SharedSlice payload) {
if (state_ != bs_none) {
return;
}
payload_ = std::move(payload);
CHECK(payload_.size() > 0);
prev_ = dynamic_cast<CatChainReceivedBlockImpl *>(chain_->create_block(std::move(block->data_->prev_)));
CHECK(prev_ != nullptr);
for (auto &X : block->data_->deps_) {
auto B = dynamic_cast<CatChainReceivedBlockImpl *>(chain_->create_block(std::move(X)));
CHECK(B != nullptr);
block_deps_.push_back(B);
}
signature_ = td::SharedSlice{block->signature_.as_slice()};
state_ = bs_initialized;
VLOG(CATCHAIN_DEBUG) << this << ": initialized payload_size=" << payload_.size();
if (prev_->is_ill()) {
set_ill();
return;
}
for (auto &X : block_deps_) {
if (X->is_ill()) {
set_ill();
return;
}
}
td::uint32 pending_deps = 0;
{
if (!prev_->delivered()) {
pending_deps++;
} else {
update_deps(prev_);
}
if (!prev_->delivered()) {
prev_->add_rev_dep(this);
}
}
for (auto &X : block_deps_) {
if (!X->delivered()) {
pending_deps++;
} else {
update_deps(X);
}
if (!X->delivered()) {
X->add_rev_dep(this);
}
}
pending_deps_ = pending_deps;
if (pending_deps_ == 0 && in_db_) {
schedule();
}
chain_->get_source(source_id_)->block_received(height_);
}
void CatChainReceivedBlockImpl::run() {
if (is_ill()) {
return;
}
if (state_ == bs_none) {
return;
}
if (state_ == bs_delivered) {
return;
}
CHECK(state_ == bs_initialized);
CHECK(!pending_deps_);
CHECK(in_db_);
initialize_fork();
pre_deliver();
deliver();
}
void CatChainReceivedBlockImpl::initialize_fork() {
CHECK(state_ == bs_initialized);
CHECK(!fork_id_);
CatChainReceiverSource *S = chain_->get_source(source_id_);
if (height_ == 1) {
fork_id_ = S->add_fork();
} else {
if (!prev_->next_) {
prev_->next_ = this;
fork_id_ = prev_->fork_id_;
} else {
fork_id_ = S->add_fork();
}
}
if (deps_.size() < fork_id_ + 1) {
deps_.resize(fork_id_ + 1, 0);
}
CHECK(deps_[fork_id_] < height_);
deps_[fork_id_] = height_;
}
void CatChainReceivedBlockImpl::pre_deliver(ton_api::catchain_block_data_fork &b) {
{
td::Status S;
S = chain_->validate_block_sync(b.left_);
if (S.is_error()) {
VLOG(CATCHAIN_WARNING) << this << ": incorrect fork blame: left is invalid: " << S.move_as_error();
set_ill();
return;
}
S = chain_->validate_block_sync(b.right_);
if (S.is_error()) {
VLOG(CATCHAIN_WARNING) << this << ": incorrect fork blame: right is invalid: " << S.move_as_error();
set_ill();
return;
}
}
// block is incorrect, since blocks are
if (b.left_->height_ != b.right_->height_ || b.left_->src_ != b.right_->src_ ||
b.left_->data_hash_ == b.right_->data_hash_) {
VLOG(CATCHAIN_WARNING) << this << ": incorrect fork blame: not a fork";
set_ill();
return;
}
auto S = chain_->get_source(b.left_->src_);
S->on_found_fork_proof(
create_serialize_tl_object<ton_api::catchain_block_data_fork>(std::move(b.left_), std::move(b.right_)));
S->blame(fork_id_, height_);
}
void CatChainReceivedBlockImpl::pre_deliver(ton_api::catchain_block_data_badBlock &b) {
}
void CatChainReceivedBlockImpl::pre_deliver(ton_api::catchain_block_data_nop &b) {
}
void CatChainReceivedBlockImpl::pre_deliver() {
if (is_ill()) {
return;
}
CHECK(state_ == bs_initialized);
CHECK(pending_deps_ == 0);
CHECK(in_db_);
auto M = chain_->get_source(source_id_);
auto d = prev_ ? &prev_->deps_ : nullptr;
for (auto &X : block_deps_) {
auto S = chain_->get_source(X->get_source_id());
auto &f = S->get_forks();
if (d) {
auto &dd = *d;
if (X->get_fork_id() < dd.size() && X->get_height() <= dd[X->get_fork_id()]) {
VLOG(CATCHAIN_WARNING) << this << ": has direct dep from source " << X->get_source_id() << " and prev block "
<< " has newer indirect dep";
set_ill();
return;
}
}
if (S->blamed() && d) {
auto &dd = *d;
for (auto x : f) {
if (x != X->get_fork_id() && dd.size() > x && dd[x] > 0) {
VLOG(CATCHAIN_WARNING) << this << ": has direct dep from source " << X->get_source_id() << " and prev block "
<< " has indirect dep to another fork of this source " << x << " " << X->get_fork_id()
<< " " << dd[x] << " " << f;
M->blame(fork_id_, height_);
set_ill();
return;
}
}
auto v = S->get_blamed_heights();
for (size_t i = 0; i < v.size() && i < dd.size(); i++) {
if (v[i] > 0 && dd[i] >= v[i]) {
VLOG(CATCHAIN_WARNING) << this << ": has direct dep from source " << X->get_source_id() << " and prev block "
<< " has indirect dep to block f" << i << "@" << v[i]
<< " which is known to blame this source";
M->blame(fork_id_, height_);
set_ill();
return;
}
}
}
}
auto X = fetch_tl_object<ton_api::catchain_block_inner_Data>(payload_.as_slice(), true);
if (X.is_error()) {
is_custom_ = true;
} else {
ton_api::downcast_call(*X.move_as_ok().get(), [Self = this](auto &obj) { Self->pre_deliver(obj); });
}
}
void CatChainReceivedBlockImpl::deliver() {
if (is_ill()) {
return;
}
CHECK(state_ == bs_initialized);
CHECK(pending_deps_ == 0);
CHECK(in_db_);
chain_->deliver_block(this);
state_ = bs_delivered;
VLOG(CATCHAIN_DEBUG) << this << ": delivered";
for (auto &B : rev_deps_) {
B->dep_delivered(this);
}
rev_deps_.clear();
chain_->get_source(source_id_)->block_delivered(height_);
}
void CatChainReceivedBlockImpl::set_ill() {
if (state_ == bs_ill) {
return;
}
VLOG(CATCHAIN_WARNING) << this << ": got ill";
auto M = chain_->get_source(source_id_);
M->blame();
state_ = bs_ill;
for (auto &B : rev_deps_) {
B->dep_ill(this);
}
}
void CatChainReceivedBlockImpl::dep_ill(CatChainReceivedBlockImpl *block) {
set_ill();
}
void CatChainReceivedBlockImpl::update_deps(CatChainReceivedBlockImpl *block) {
auto &d = block->deps_;
if (d.size() > deps_.size()) {
deps_.resize(d.size(), 0);
}
for (size_t i = 0; i < d.size(); i++) {
if (deps_[i] < d[i]) {
deps_[i] = d[i];
}
}
}
void CatChainReceivedBlockImpl::dep_delivered(CatChainReceivedBlockImpl *block) {
if (is_ill()) {
return;
}
CHECK(!block->is_ill());
update_deps(block);
pending_deps_--;
if (pending_deps_ == 0 && in_db_) {
schedule();
}
}
void CatChainReceivedBlockImpl::written() {
if (!in_db_) {
in_db_ = true;
if (pending_deps_ == 0) {
schedule();
}
}
}
void CatChainReceivedBlockImpl::add_rev_dep(CatChainReceivedBlockImpl *block) {
rev_deps_.push_back(block);
}
CatChainReceivedBlock *CatChainReceivedBlockImpl::get_prev() const {
return prev_;
}
CatChainBlockHash CatChainReceivedBlockImpl::get_prev_hash() const {
CHECK(prev_);
return prev_->get_hash();
}
std::vector<CatChainBlockHash> CatChainReceivedBlockImpl::get_dep_hashes() const {
std::vector<CatChainBlockHash> v;
v.resize(block_deps_.size());
for (size_t i = 0; i < v.size(); i++) {
v[i] = block_deps_[i]->get_hash();
}
return v;
}
void CatChainReceivedBlockImpl::schedule() {
chain_->run_block(this);
}
void CatChainReceivedBlockImpl::find_pending_deps(std::vector<CatChainBlockHash> &vec, td::uint32 max_size) const {
if (height_ == 0 || is_ill() || delivered() || vec.size() == max_size) {
return;
}
if (!initialized()) {
vec.push_back(get_hash());
return;
}
if (prev_) {
prev_->find_pending_deps(vec, max_size);
}
for (auto &X : block_deps_) {
X->find_pending_deps(vec, max_size);
}
}
tl_object_ptr<ton_api::catchain_block_id> CatChainReceivedBlock::block_id(CatChainReceiver *chain,
tl_object_ptr<ton_api::catchain_block> &block,
td::Slice payload) {
return create_tl_object<ton_api::catchain_block_id>(block->incarnation_, chain->get_source_hash(block->src_).tl(),
block->height_, sha256_bits256(payload));
}
tl_object_ptr<ton_api::catchain_block_id> CatChainReceivedBlock::block_id(
CatChainReceiver *chain, tl_object_ptr<ton_api::catchain_block_dep> &block) {
return create_tl_object<ton_api::catchain_block_id>(
chain->get_incarnation(), chain->get_source_hash(block->src_).tl(), block->height_, block->data_hash_);
}
CatChainBlockHash CatChainReceivedBlock::block_hash(CatChainReceiver *chain,
tl_object_ptr<ton_api::catchain_block> &block, td::Slice payload) {
return get_tl_object_sha_bits256(block_id(chain, block, payload));
}
CatChainBlockHash CatChainReceivedBlock::block_hash(CatChainReceiver *chain,
tl_object_ptr<ton_api::catchain_block_dep> &block) {
return get_tl_object_sha_bits256(block_id(chain, block));
}
td::Status CatChainReceivedBlock::pre_validate_block(CatChainReceiver *chain,
tl_object_ptr<ton_api::catchain_block> &block, td::Slice payload) {
CHECK(block->incarnation_ == chain->get_incarnation());
if (block->height_ <= 0) {
return td::Status::Error(ErrorCode::protoviolation, std::string("bad height ") + std::to_string(block->height_));
}
if (block->src_ < 0 || static_cast<td::uint32>(block->src_) >= chain->get_sources_cnt()) {
return td::Status::Error(ErrorCode::protoviolation, std::string("bad src ") + std::to_string(block->src_));
}
if (block->data_->prev_->src_ < 0) {
return td::Status::Error(ErrorCode::protoviolation,
std::string("bad prev block src ") + std::to_string(block->data_->prev_->src_));
}
if (block->data_->deps_.size() > chain->opts().max_deps) {
return td::Status::Error(ErrorCode::protoviolation, std::string("too many deps"));
}
auto prev_src = static_cast<td::uint32>(block->data_->prev_->src_);
if (block->height_ > 1) {
if (prev_src != static_cast<td::uint32>(block->src_)) {
return td::Status::Error(ErrorCode::protoviolation,
std::string("bad prev block src ") + std::to_string(block->data_->prev_->src_));
}
} else {
if (prev_src != chain->get_sources_cnt()) {
return td::Status::Error(ErrorCode::protoviolation,
std::string("bad prev(first) block src ") + std::to_string(block->data_->prev_->src_));
}
}
if (block->data_->prev_->height_ + 1 != block->height_) {
return td::Status::Error(ErrorCode::protoviolation, std::string("bad prev block height ") +
std::to_string(block->data_->prev_->height_) + " (our " +
std::to_string(block->height_) + ")");
}
std::set<td::uint32> used;
used.insert(block->src_);
for (auto &X : block->data_->deps_) {
if (used.find(X->src_) != used.end()) {
return td::Status::Error(ErrorCode::protoviolation, "two deps from same source");
}
used.insert(X->src_);
}
TRY_STATUS(chain->validate_block_sync(block->data_->prev_));
for (auto &X : block->data_->deps_) {
TRY_STATUS(chain->validate_block_sync(X));
}
if (payload.size() == 0) {
return td::Status::Error(ErrorCode::protoviolation, "empty payload");
}
return td::Status::OK();
}
td::Status CatChainReceivedBlock::pre_validate_block(CatChainReceiver *chain,
tl_object_ptr<ton_api::catchain_block_dep> &block) {
if (block->height_ < 0) {
return td::Status::Error(ErrorCode::protoviolation, std::string("bad height ") + std::to_string(block->height_));
}
if (block->height_ > 0) {
if (block->src_ < 0 || static_cast<td::uint32>(block->src_) >= chain->get_sources_cnt()) {
return td::Status::Error(ErrorCode::protoviolation, std::string("bad src ") + std::to_string(block->src_));
}
} else {
if (block->src_ < 0 || static_cast<td::uint32>(block->src_) != chain->get_sources_cnt()) {
return td::Status::Error(ErrorCode::protoviolation,
std::string("bad src (first block) ") + std::to_string(block->src_));
}
if (block->data_hash_ != chain->get_incarnation() || block->signature_.size() != 0) {
return td::Status::Error(ErrorCode::protoviolation, std::string("bad first block"));
}
}
return td::Status::OK();
}
tl_object_ptr<ton_api::catchain_block> CatChainReceivedBlockImpl::export_tl() const {
CHECK(initialized());
CHECK(height_ > 0);
std::vector<tl_object_ptr<ton_api::catchain_block_dep>> deps;
for (auto &B : block_deps_) {
deps.push_back(B->export_tl_dep());
}
return create_tl_object<ton_api::catchain_block>(
chain_->get_incarnation(), source_id_, height_,
create_tl_object<ton_api::catchain_block_data>(prev_->export_tl_dep(), std::move(deps)),
signature_.clone_as_buffer_slice());
}
tl_object_ptr<ton_api::catchain_block_dep> CatChainReceivedBlockImpl::export_tl_dep() const {
return create_tl_object<ton_api::catchain_block_dep>(source_id_, height_, data_hash_,
signature_.clone_as_buffer_slice());
}
CatChainReceivedBlockImpl::CatChainReceivedBlockImpl(td::uint32 source_id, CatChainSessionId hash,
CatChainReceiver *chain) {
chain_ = chain;
state_ = bs_delivered;
fork_id_ = 0;
source_id_ = source_id;
data_ = nullptr;
prev_ = nullptr;
height_ = 0;
data_hash_ = hash;
hash_ = get_tl_object_sha_bits256(create_tl_object<ton_api::catchain_block_id>(
chain->get_incarnation(), chain->get_incarnation(), height_, data_hash_));
}
CatChainReceivedBlockImpl::CatChainReceivedBlockImpl(tl_object_ptr<ton_api::catchain_block> block,
td::SharedSlice payload, CatChainReceiver *chain) {
chain_ = chain;
data_hash_ = sha256_bits256(payload.as_slice());
hash_ = get_tl_object_sha_bits256(create_tl_object<ton_api::catchain_block_id>(
block->incarnation_, chain->get_source_hash(block->src_).tl(), block->height_, data_hash_));
height_ = block->height_;
source_id_ = block->src_;
auto S = chain_->get_source(source_id_);
S->on_new_block(this);
initialize(std::move(block), std::move(payload));
}
CatChainReceivedBlockImpl::CatChainReceivedBlockImpl(tl_object_ptr<ton_api::catchain_block_dep> block,
CatChainReceiver *chain) {
chain_ = chain;
data_hash_ = block->data_hash_;
source_id_ = block->src_;
signature_ = td::SharedSlice{block->signature_.as_slice()};
hash_ = get_tl_object_sha_bits256(create_tl_object<ton_api::catchain_block_id>(
chain_->get_incarnation(), chain_->get_source_hash(source_id_).tl(), block->height_, data_hash_));
height_ = block->height_;
auto S = chain_->get_source(source_id_);
S->on_new_block(this);
}
std::unique_ptr<CatChainReceivedBlock> CatChainReceivedBlock::create(tl_object_ptr<ton_api::catchain_block> block,
td::SharedSlice payload, CatChainReceiver *chain) {
return std::make_unique<CatChainReceivedBlockImpl>(std::move(block), std::move(payload), chain);
}
std::unique_ptr<CatChainReceivedBlock> CatChainReceivedBlock::create(tl_object_ptr<ton_api::catchain_block_dep> block,
CatChainReceiver *chain) {
return std::make_unique<CatChainReceivedBlockImpl>(std::move(block), chain);
}
std::unique_ptr<CatChainReceivedBlock> CatChainReceivedBlock::create_root(td::uint32 source_id,
CatChainSessionId data_hash,
CatChainReceiver *chain) {
return std::make_unique<CatChainReceivedBlockImpl>(source_id, data_hash, chain);
}
} // namespace catchain
} // namespace ton

View file

@ -0,0 +1,114 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/buffer.h"
#include "td/utils/SharedSlice.h"
#include "auto/tl/ton_api.h"
#include "catchain/catchain-receiver.h"
namespace ton {
namespace catchain {
class CatChainReceiver;
class CatChainReceiverSource;
class CatChainReceiverFork;
class CatChainReceivedBlock {
public:
// getters
virtual const td::SharedSlice &get_payload() const = 0;
virtual CatChainBlockHash get_hash() const = 0;
virtual const td::SharedSlice &get_signature() const = 0;
virtual CatChainBlockHeight get_height() const = 0;
virtual CatChainReceivedBlock *get_prev() const = 0;
virtual CatChainBlockHash get_prev_hash() const = 0;
virtual const std::vector<CatChainBlockHeight> &get_deps() const = 0;
virtual std::vector<CatChainBlockHash> get_dep_hashes() const = 0;
virtual CatChainReceiver *get_chain() const = 0;
virtual td::uint32 get_fork_id() const = 0;
virtual td::uint32 get_source_id() const = 0;
virtual tl_object_ptr<ton_api::catchain_block> export_tl() const = 0;
virtual tl_object_ptr<ton_api::catchain_block_dep> export_tl_dep() const = 0;
virtual void find_pending_deps(std::vector<CatChainBlockHash> &vec, td::uint32 max_size) const = 0;
public:
// state
virtual bool initialized() const = 0;
virtual bool delivered() const = 0;
virtual bool is_ill() const = 0;
virtual bool is_custom() const = 0;
virtual bool in_db() const = 0;
public:
//change state
virtual void initialize(tl_object_ptr<ton_api::catchain_block> block, td::SharedSlice payload) = 0;
virtual void set_ill() = 0;
virtual void written() = 0;
virtual void run() = 0;
public:
static std::unique_ptr<CatChainReceivedBlock> create(tl_object_ptr<ton_api::catchain_block> block,
td::SharedSlice payload, CatChainReceiver *chain);
static std::unique_ptr<CatChainReceivedBlock> create(tl_object_ptr<ton_api::catchain_block_dep> block,
CatChainReceiver *chain);
static std::unique_ptr<CatChainReceivedBlock> create_root(td::uint32 source_id, CatChainBlockPayloadHash data_hash,
CatChainReceiver *chain);
static tl_object_ptr<ton_api::catchain_block_id> block_id(CatChainReceiver *chain,
tl_object_ptr<ton_api::catchain_block> &block,
td::Slice payload);
static tl_object_ptr<ton_api::catchain_block_id> block_id(CatChainReceiver *chain,
tl_object_ptr<ton_api::catchain_block_dep> &block);
static CatChainBlockHash block_hash(CatChainReceiver *chain, tl_object_ptr<ton_api::catchain_block> &block,
td::Slice payload);
static CatChainBlockHash block_hash(CatChainReceiver *chain, tl_object_ptr<ton_api::catchain_block_dep> &block);
static td::Status pre_validate_block(CatChainReceiver *chain, tl_object_ptr<ton_api::catchain_block> &block,
td::Slice payload);
static td::Status pre_validate_block(CatChainReceiver *chain, tl_object_ptr<ton_api::catchain_block_dep> &block);
virtual ~CatChainReceivedBlock() = default;
};
} // namespace catchain
} // namespace ton
namespace td {
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceivedBlock &block) {
sb << "[block " << block.get_chain()->get_incarnation() << " " << block.get_source_id() << " " << block.get_fork_id()
<< " " << block.get_hash() << "]";
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceivedBlock *block) {
sb << *block;
return sb;
}
} // namespace td

View file

@ -0,0 +1,179 @@
/*
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 "catchain/catchain-received-block.h"
namespace ton {
namespace catchain {
class CatChainReceiver;
class CatChainReceiverSource;
class CatChainReceiverFork;
class CatChainReceivedBlockImpl : public CatChainReceivedBlock {
public:
const td::SharedSlice &get_payload() const override {
return payload_;
}
CatChainBlockHash get_hash() const override {
return hash_;
}
const td::SharedSlice &get_signature() const override {
return signature_;
}
CatChainBlockHeight get_height() const override {
return height_;
}
CatChainReceivedBlock *get_prev() const override;
CatChainBlockHash get_prev_hash() const override;
const std::vector<CatChainBlockHeight> &get_deps() const override {
return deps_;
}
std::vector<CatChainBlockHash> get_dep_hashes() const override;
CatChainReceiver *get_chain() const override {
return chain_;
}
td::uint32 get_fork_id() const override {
return fork_id_;
}
td::uint32 get_source_id() const override {
return source_id_;
}
tl_object_ptr<ton_api::catchain_block> export_tl() const override;
tl_object_ptr<ton_api::catchain_block_dep> export_tl_dep() const override;
void find_pending_deps(std::vector<CatChainBlockHash> &vec, td::uint32 max_size) const override;
public:
bool initialized() const override {
return state_ >= bs_initialized;
}
bool delivered() const override {
return state_ >= bs_delivered;
}
bool is_ill() const override {
return state_ == bs_ill;
}
bool is_custom() const override {
return is_custom_;
}
bool in_db() const override {
return in_db_;
}
public:
void initialize(tl_object_ptr<ton_api::catchain_block> block, td::SharedSlice payload) override;
void run() override;
void pre_deliver(ton_api::catchain_block_data_fork &b);
void pre_deliver(ton_api::catchain_block_data_badBlock &b);
void pre_deliver(ton_api::catchain_block_data_nop &b);
template <class T>
void pre_deliver(T &b) {
// do nothing, it is custom block
is_custom_ = true;
}
void pre_deliver();
void deliver();
void dep_delivered(CatChainReceivedBlockImpl *block);
void dep_ill(CatChainReceivedBlockImpl *block);
void set_ill() override;
void schedule();
void written() override;
public:
CatChainReceivedBlockImpl(tl_object_ptr<ton_api::catchain_block> block, td::SharedSlice payload,
CatChainReceiver *chain);
CatChainReceivedBlockImpl(tl_object_ptr<ton_api::catchain_block_dep> block, CatChainReceiver *chain);
CatChainReceivedBlockImpl(td::uint32 source_id, CatChainSessionId hash, CatChainReceiver *chain);
private:
enum State {
bs_none,
bs_ill,
bs_initialized,
bs_delivered,
} state_ = bs_none;
void update_deps(CatChainReceivedBlockImpl *block);
void add_rev_dep(CatChainReceivedBlockImpl *block);
void add_child_dep(CatChainReceivedBlockImpl *block);
void initialize_fork();
void on_ready_to_deliver();
td::uint32 fork_id_{0};
td::uint32 source_id_;
CatChainReceiver *chain_;
tl_object_ptr<ton_api::catchain_block_inner_Data> data_;
td::SharedSlice payload_;
CatChainBlockHash hash_;
CatChainBlockPayloadHash data_hash_;
CatChainReceivedBlockImpl *prev_;
CatChainBlockHeight height_;
CatChainReceivedBlockImpl *next_ = nullptr;
std::vector<CatChainReceivedBlockImpl *> block_deps_;
std::vector<CatChainBlockHeight> deps_;
td::SharedSlice signature_;
std::vector<CatChainReceivedBlockImpl *> rev_deps_;
td::uint32 pending_deps_ = 0;
bool is_custom_ = false;
bool in_db_ = false;
};
} // namespace catchain
} // namespace ton
namespace td {
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceivedBlockImpl &block) {
sb << "[block " << block.get_chain()->get_incarnation() << " " << block.get_source_id() << " " << block.get_fork_id()
<< " " << block.get_hash() << "]";
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceivedBlockImpl *block) {
sb << *block;
return sb;
}
} // namespace td

View file

@ -0,0 +1,70 @@
/*
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/actor/actor.h"
#include "adnl/adnl.h"
#include "overlay/overlays.h"
#include "catchain-types.h"
namespace ton {
namespace catchain {
class CatChainReceiverInterface : public td::actor::Actor {
public:
class Callback {
public:
virtual void new_block(td::uint32 src_id, td::uint32 fork_id, CatChainBlockHash hash, CatChainBlockHeight height,
CatChainBlockHash prev, std::vector<CatChainBlockHash> deps,
std::vector<CatChainBlockHeight> vt, td::SharedSlice data) = 0;
virtual void blame(td::uint32 src_id) = 0;
virtual void on_custom_message(PublicKeyHash src, td::BufferSlice data) = 0;
virtual void on_custom_query(PublicKeyHash src, td::BufferSlice data, td::Promise<td::BufferSlice> promise) = 0;
virtual void on_broadcast(PublicKeyHash src, td::BufferSlice data) = 0;
virtual void start() = 0;
virtual ~Callback() = default;
};
virtual void add_block(td::BufferSlice payload, std::vector<CatChainBlockHash> deps) = 0;
virtual void debug_add_fork(td::BufferSlice payload, CatChainBlockHeight height,
std::vector<CatChainBlockHash> deps) = 0;
virtual void blame_node(td::uint32 idx) = 0;
virtual void send_fec_broadcast(td::BufferSlice data) = 0;
virtual void send_custom_query_data(PublicKeyHash dst, std::string name, td::Promise<td::BufferSlice> promise,
td::Timestamp timeout, td::BufferSlice query) = 0;
virtual void send_custom_query_data_via(PublicKeyHash dst, std::string name, td::Promise<td::BufferSlice> promise,
td::Timestamp timeout, td::BufferSlice query, td::uint64 max_answer_size,
td::actor::ActorId<adnl::AdnlSenderInterface> via) = 0;
virtual void send_custom_message_data(PublicKeyHash dst, td::BufferSlice query) = 0;
virtual void destroy() = 0;
static td::actor::ActorOwn<CatChainReceiverInterface> create(std::unique_ptr<Callback> callback, CatChainOptions opts,
td::actor::ActorId<keyring::Keyring> keyring,
td::actor::ActorId<adnl::Adnl> adnl,
td::actor::ActorId<overlay::Overlays> overlay_manager,
std::vector<CatChainNode> ids, PublicKeyHash local_id,
CatChainSessionId unique_hash, std::string db_root);
virtual ~CatChainReceiverInterface() = default;
};
} // namespace catchain
} // namespace ton

View file

@ -0,0 +1,179 @@
/*
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 "catchain-receiver-source.hpp"
#include "common/errorlog.h"
namespace ton {
namespace catchain {
td::uint32 CatChainReceiverSourceImpl::add_fork() {
if (fork_ids_.size() > 0) {
blame();
}
auto F = chain_->add_fork();
CHECK(F > 0);
fork_ids_.push_back(F);
VLOG(CATCHAIN_INFO) << this << ": adding new fork " << F << " of source " << id_;
if (fork_ids_.size() > 1) {
CHECK(blamed());
}
return F;
}
CatChainReceiverSourceImpl::CatChainReceiverSourceImpl(CatChainReceiver *chain, PublicKey source,
adnl::AdnlNodeIdShort adnl_id, td::uint32 id)
: chain_(chain), id_(id), adnl_id_(adnl_id) {
src_ = source.compute_short_id();
encryptor_ = source.create_encryptor_async().move_as_ok();
encryptor_sync_ = source.create_encryptor().move_as_ok();
full_id_ = std::move(source);
}
td::Result<std::unique_ptr<CatChainReceiverSource>> CatChainReceiverSource::create(CatChainReceiver *chain,
PublicKey source,
adnl::AdnlNodeIdShort adnl_id,
td::uint32 id) {
return std::make_unique<CatChainReceiverSourceImpl>(chain, std::move(source), adnl_id, id);
}
void CatChainReceiverSourceImpl::blame(td::uint32 fork, CatChainBlockHeight height) {
blame();
if (blamed_heights_.size() > 0) {
if (blamed_heights_.size() <= fork) {
blamed_heights_.resize(fork + 1, 0);
}
if (blamed_heights_[fork] == 0 || blamed_heights_[fork] > height) {
VLOG(CATCHAIN_INFO) << this << ": blamed at " << fork << " " << height;
blamed_heights_[fork] = height;
}
}
}
void CatChainReceiverSourceImpl::blame() {
if (!blamed_) {
LOG(ERROR) << this << ": CATCHAIN: blaming source " << id_;
blocks_.clear();
chain_->on_blame(id_);
}
blamed_ = true;
}
CatChainReceivedBlock *CatChainReceiverSourceImpl::get_block(CatChainBlockHeight height) const {
auto it = blocks_.find(height);
if (it != blocks_.end()) {
return it->second;
} else {
return nullptr;
}
}
void CatChainReceiverSourceImpl::block_received(CatChainBlockHeight height) {
if (blamed()) {
return;
}
if (received_height_ + 1 == height) {
received_height_ = height;
}
while (true) {
auto it = blocks_.find(received_height_ + 1);
if (it == blocks_.end()) {
return;
}
if (!it->second->initialized()) {
return;
}
received_height_++;
}
}
void CatChainReceiverSourceImpl::block_delivered(CatChainBlockHeight height) {
if (blamed()) {
return;
}
if (delivered_height_ + 1 == height) {
delivered_height_ = height;
}
while (true) {
auto it = blocks_.find(delivered_height_ + 1);
if (it == blocks_.end()) {
return;
}
if (!it->second->delivered()) {
return;
}
delivered_height_++;
}
}
td::Status CatChainReceiverSourceImpl::validate_dep_sync(tl_object_ptr<ton_api::catchain_block_dep> &dep) {
auto S = std::move(dep->signature_);
auto str = serialize_tl_object(dep, true);
dep->signature_ = std::move(S);
auto R = encryptor_sync_->check_signature(str.as_slice(), dep->signature_.as_slice());
if (R.is_error()) {
return R.move_as_error();
}
return td::Status::OK();
}
void CatChainReceiverSourceImpl::on_new_block(CatChainReceivedBlock *block) {
if (fork_is_found()) {
return;
}
CHECK(block->get_source_id() == id_);
auto it = blocks_.find(block->get_height());
if (it != blocks_.end()) {
CHECK(block->get_hash() != it->second->get_hash());
VLOG(CATCHAIN_WARNING) << this << ": found fork on height " << block->get_height();
if (!fork_is_found()) {
on_found_fork_proof(create_serialize_tl_object<ton_api::catchain_block_data_fork>(block->export_tl_dep(),
it->second->export_tl_dep())
.as_slice());
chain_->add_prepared_event(fork_proof());
}
blame();
return;
}
blocks_[block->get_height()] = block;
}
void CatChainReceiverSourceImpl::on_found_fork_proof(td::Slice proof) {
if (!fork_is_found()) {
fetch_tl_object<ton_api::catchain_block_data_fork>(proof, true).ensure();
fork_proof_ = td::SharedSlice{proof};
errorlog::ErrorLog::log(PSTRING() << "catchain " << chain_->get_incarnation() << " source " << id_
<< " found fork. hash=" << sha256_bits256(fork_proof_.as_slice()).to_hex());
errorlog::ErrorLog::log_file(fork_proof_.clone_as_buffer_slice());
}
}
} // namespace catchain
} // namespace ton

View file

@ -0,0 +1,85 @@
/*
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 "catchain-receiver.h"
#include "keys/encryptor.h"
namespace ton {
namespace catchain {
class CatChainReceiver;
class CatChainReceivedBlock;
class CatChainReceiverSource {
public:
virtual td::uint32 get_id() const = 0;
virtual PublicKeyHash get_hash() const = 0;
virtual PublicKey get_full_id() const = 0;
virtual adnl::AdnlNodeIdShort get_adnl_id() const = 0;
virtual td::uint32 add_fork() = 0;
virtual bool blamed() const = 0;
virtual void blame(td::uint32 fork, CatChainBlockHeight height) = 0;
virtual void blame() = 0;
virtual const std::vector<td::uint32> &get_forks() const = 0;
virtual const std::vector<CatChainBlockHeight> &get_blamed_heights() const = 0;
virtual Encryptor *get_encryptor_sync() const = 0;
virtual td::uint32 get_forks_cnt() const = 0;
virtual CatChainBlockHeight delivered_height() const = 0;
virtual CatChainBlockHeight received_height() const = 0;
virtual CatChainReceivedBlock *get_block(CatChainBlockHeight height) const = 0;
virtual void block_received(CatChainBlockHeight height) = 0;
virtual void block_delivered(CatChainBlockHeight height) = 0;
virtual td::Status validate_dep_sync(tl_object_ptr<ton_api::catchain_block_dep> &dep) = 0;
virtual void on_new_block(CatChainReceivedBlock *block) = 0;
virtual void on_found_fork_proof(td::Slice fork) = 0;
virtual td::BufferSlice fork_proof() const = 0;
virtual bool fork_is_found() const = 0;
static td::Result<std::unique_ptr<CatChainReceiverSource>> create(CatChainReceiver *chain, PublicKey pub_key,
adnl::AdnlNodeIdShort adnl_id, td::uint32 id);
virtual CatChainReceiver *get_chain() const = 0;
virtual ~CatChainReceiverSource() = default;
};
} // namespace catchain
} // namespace ton
namespace td {
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceiverSource &source) {
sb << "[source " << source.get_chain()->get_incarnation() << " " << source.get_id() << "]";
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceiverSource *source) {
sb << *source;
return sb;
}
} // namespace td

View file

@ -0,0 +1,137 @@
/*
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 <map>
#include "catchain-receiver-source.h"
#include "catchain-receiver.h"
#include "catchain-received-block.h"
namespace ton {
namespace catchain {
class CatChainReceiverSourceImpl : public CatChainReceiverSource {
public:
td::uint32 get_id() const override {
return id_;
}
PublicKeyHash get_hash() const override {
return src_;
}
PublicKey get_full_id() const override {
return full_id_;
}
adnl::AdnlNodeIdShort get_adnl_id() const override {
return adnl_id_;
}
td::uint32 add_fork() override;
bool blamed() const override {
return blamed_;
}
void blame(td::uint32 fork, CatChainBlockHeight height) override;
void blame() override;
void block_received(CatChainBlockHeight height) override;
void block_delivered(CatChainBlockHeight height) override;
const std::vector<td::uint32> &get_forks() const override {
return fork_ids_;
}
const std::vector<CatChainBlockHeight> &get_blamed_heights() const override {
return blamed_heights_;
}
td::actor::ActorId<EncryptorAsync> get_encryptor() const {
return encryptor_.get();
}
Encryptor *get_encryptor_sync() const override {
return encryptor_sync_.get();
}
td::uint32 get_forks_cnt() const override {
return static_cast<td::uint32>(fork_ids_.size());
}
CatChainBlockHeight delivered_height() const override {
return delivered_height_;
}
CatChainBlockHeight received_height() const override {
return received_height_;
}
CatChainReceivedBlock *get_block(CatChainBlockHeight height) const override;
td::Status validate_dep_sync(tl_object_ptr<ton_api::catchain_block_dep> &dep) override;
void on_new_block(CatChainReceivedBlock *block) override;
void on_found_fork_proof(td::Slice proof) override;
bool fork_is_found() const override {
return !fork_proof_.empty();
}
td::BufferSlice fork_proof() const override {
if (!fork_proof_.empty()) {
return fork_proof_.clone_as_buffer_slice();
} else {
return td::BufferSlice();
}
}
CatChainReceiver *get_chain() const override {
return chain_;
}
CatChainReceiverSourceImpl(CatChainReceiver *chain, PublicKey source, adnl::AdnlNodeIdShort adnl_id, td::uint32 id);
private:
CatChainReceiver *chain_;
td::uint32 id_;
PublicKeyHash src_;
bool blamed_ = false;
PublicKey full_id_;
adnl::AdnlNodeIdShort adnl_id_;
std::vector<td::uint32> fork_ids_;
td::actor::ActorOwn<EncryptorAsync> encryptor_;
std::unique_ptr<Encryptor> encryptor_sync_;
std::vector<CatChainBlockHeight> blamed_heights_;
std::map<CatChainBlockHeight, CatChainReceivedBlock *> blocks_;
td::SharedSlice fork_proof_;
CatChainBlockHeight delivered_height_ = 0;
CatChainBlockHeight received_height_ = 0;
};
} // namespace catchain
} // namespace ton
namespace td {
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceiverSourceImpl &source) {
sb << "[source " << source.get_chain()->get_incarnation() << " " << source.get_id() << "]";
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceiverSourceImpl *source) {
sb << *source;
return sb;
}
} // namespace td

View file

@ -0,0 +1,988 @@
/*
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 <set>
#include "td/actor/PromiseFuture.h"
#include "td/utils/Random.h"
#include "td/db/RocksDb.h"
#include "td/utils/port/path.h"
#include "td/utils/overloaded.h"
#include "common/delay.h"
#include "catchain-receiver.hpp"
namespace ton {
namespace catchain {
PublicKeyHash CatChainReceiverImpl::get_source_hash(td::uint32 source_id) const {
CHECK(source_id < sources_.size());
return sources_[source_id]->get_hash();
}
td::uint32 CatChainReceiverImpl::add_fork() {
return ++total_forks_;
}
void CatChainReceiverImpl::deliver_block(CatChainReceivedBlock *block) {
VLOG(CATCHAIN_INFO) << this << ": delivering block " << block->get_hash() << " src=" << block->get_source_id()
<< " fork=" << block->get_fork_id() << " height=" << block->get_height()
<< " custom=" << block->is_custom();
callback_->new_block(block->get_source_id(), block->get_fork_id(), block->get_hash(), block->get_height(),
block->get_height() == 1 ? CatChainBlockHash::zero() : block->get_prev_hash(),
block->get_dep_hashes(), block->get_deps(),
block->is_custom() ? block->get_payload().clone() : td::SharedSlice());
std::vector<adnl::AdnlNodeIdShort> v;
for (auto it : neighbours_) {
auto S = get_source(it);
v.push_back(S->get_adnl_id());
}
auto update = create_tl_object<ton_api::catchain_blockUpdate>(block->export_tl());
auto D = serialize_tl_object(update, true, block->get_payload().as_slice());
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_multiple_messages, std::move(v),
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(D));
}
void CatChainReceiverImpl::receive_block(adnl::AdnlNodeIdShort src, tl_object_ptr<ton_api::catchain_block> block,
td::BufferSlice payload) {
auto id = CatChainReceivedBlock::block_hash(this, block, payload);
auto B = get_block(id);
if (B && B->initialized()) {
return;
}
if (block->incarnation_ != incarnation_) {
VLOG(CATCHAIN_WARNING) << this << ": dropping broken block from " << src << ": bad incarnation "
<< block->incarnation_;
return;
}
auto S = validate_block_sync(block, payload.as_slice());
if (S.is_error()) {
VLOG(CATCHAIN_WARNING) << this << ": received broken block from " << src << ": " << S.move_as_error();
return;
}
auto raw_data = serialize_tl_object(block, true, payload.as_slice());
create_block(std::move(block), td::SharedSlice{payload.as_slice()});
if (!opts_.debug_disable_db) {
db_.set(
id, std::move(raw_data), [](td::Unit) {}, 1.0);
}
block_written_to_db(id);
}
void CatChainReceiverImpl::receive_block_answer(adnl::AdnlNodeIdShort src, td::BufferSlice data) {
auto F = fetch_tl_prefix<ton_api::catchain_BlockResult>(data, true);
if (F.is_error()) {
VLOG(CATCHAIN_INFO) << this << ": received bad block result: " << F.move_as_error();
return;
}
auto f = F.move_as_ok();
ton_api::downcast_call(
*f.get(),
td::overloaded(
[&](ton_api::catchain_blockNotFound &r) { VLOG(CATCHAIN_INFO) << this << ": catchain block not found"; },
[&](ton_api::catchain_blockResult &r) { receive_block(src, std::move(r.block_), std::move(data)); }));
}
void CatChainReceiverImpl::receive_message_from_overlay(adnl::AdnlNodeIdShort src, td::BufferSlice data) {
if (!read_db_) {
return;
}
/*auto S = get_source_by_hash(src);
CHECK(S != nullptr);
if (S->blamed()) {
VLOG(CATCHAIN_INFO) << this << ": dropping block update from blamed source " << src;
return;
}*/
auto R = fetch_tl_prefix<ton_api::catchain_blockUpdate>(data, true);
if (R.is_error()) {
VLOG(CATCHAIN_WARNING) << this << ": dropping broken block from " << src << ": " << R.move_as_error();
return;
}
auto U = R.move_as_ok();
receive_block(src, std::move(U->block_), std::move(data));
}
void CatChainReceiverImpl::receive_broadcast_from_overlay(PublicKeyHash src, td::BufferSlice data) {
if (!read_db_) {
return;
}
callback_->on_broadcast(src, std::move(data));
}
/*void CatChainReceiverImpl::send_block(PublicKeyHash src, tl_object_ptr<ton_api::catchain_block> block,
td::BufferSlice payload) {
CHECK(read_db_);
CHECK(src == local_id_);
validate_block_sync(block, payload.as_slice()).ensure();
auto B = create_block(std::move(block), td::SharedSlice{payload.as_slice()});
CHECK(B != nullptr);
run_scheduler();
CHECK(B->delivered());
}*/
CatChainReceivedBlock *CatChainReceiverImpl::create_block(tl_object_ptr<ton_api::catchain_block> block,
td::SharedSlice payload) {
if (block->height_ == 0) {
return root_block_;
}
auto hash = CatChainReceivedBlock::block_hash(this, block, payload.as_slice());
auto it = blocks_.find(hash);
if (it != blocks_.end()) {
if (!it->second->initialized()) {
it->second->initialize(std::move(block), std::move(payload));
}
return it->second.get();
} else {
blocks_.emplace(hash, CatChainReceivedBlock::create(std::move(block), std::move(payload), this));
it = blocks_.find(hash);
return it->second.get();
}
}
CatChainReceivedBlock *CatChainReceiverImpl::create_block(tl_object_ptr<ton_api::catchain_block_dep> block) {
if (block->height_ == 0) {
return root_block_;
}
auto hash = CatChainReceivedBlock::block_hash(this, block);
auto it = blocks_.find(hash);
if (it != blocks_.end()) {
return it->second.get();
} else {
blocks_.emplace(hash, CatChainReceivedBlock::create(std::move(block), this));
it = blocks_.find(hash);
return it->second.get();
}
}
td::Status CatChainReceiverImpl::validate_block_sync(tl_object_ptr<ton_api::catchain_block_dep> &dep) {
TRY_STATUS_PREFIX(CatChainReceivedBlock::pre_validate_block(this, dep), "failed to validate block: ");
if (dep->height_ > 0) {
auto id = CatChainReceivedBlock::block_id(this, dep);
auto B = serialize_tl_object(id, true);
auto block = get_block(get_tl_object_sha_bits256(id));
if (block) {
return td::Status::OK();
}
auto S = get_source_by_hash(PublicKeyHash{id->src_});
CHECK(S != nullptr);
auto E = S->get_encryptor_sync();
CHECK(E != nullptr);
return E->check_signature(B.as_slice(), dep->signature_.as_slice());
} else {
return td::Status::OK();
}
}
td::Status CatChainReceiverImpl::validate_block_sync(tl_object_ptr<ton_api::catchain_block> &block, td::Slice payload) {
//LOG(INFO) << ton_api::to_string(block);
TRY_STATUS_PREFIX(CatChainReceivedBlock::pre_validate_block(this, block, payload), "failed to validate block: ");
if (block->height_ > 0) {
auto id = CatChainReceivedBlock::block_id(this, block, payload);
auto B = serialize_tl_object(id, true);
auto S = get_source_by_hash(PublicKeyHash{id->src_});
CHECK(S != nullptr);
auto E = S->get_encryptor_sync();
CHECK(E != nullptr);
return E->check_signature(B.as_slice(), block->signature_.as_slice());
} else {
return td::Status::OK();
}
}
void CatChainReceiverImpl::run_scheduler() {
while (!to_run_.empty()) {
auto B = to_run_.front();
to_run_.pop_front();
B->run();
}
}
void CatChainReceiverImpl::run_block(CatChainReceivedBlock *block) {
to_run_.push_back(block);
}
CatChainReceivedBlock *CatChainReceiverImpl::get_block(CatChainBlockHash hash) const {
auto it = blocks_.find(hash);
if (it == blocks_.end()) {
return nullptr;
} else {
return it->second.get();
}
}
void CatChainReceiverImpl::add_block_cont_3(tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload) {
last_sent_block_ = create_block(std::move(block), td::SharedSlice{payload.as_slice()});
last_sent_block_->written();
run_scheduler();
if (!intentional_fork_) {
CHECK(last_sent_block_->delivered());
}
active_send_ = false;
if (pending_blocks_.size() > 0) {
auto B = std::move(pending_blocks_.front());
pending_blocks_.pop_front();
add_block(std::move(B->payload_), std::move(B->deps_));
}
}
void CatChainReceiverImpl::add_block_cont_2(tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload) {
if (opts_.debug_disable_db) {
add_block_cont_3(std::move(block), std::move(payload));
return;
}
auto id = CatChainReceivedBlock::block_hash(this, block, payload);
td::BufferSlice raw_data{32};
raw_data.as_slice().copy_from(as_slice(id));
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), block = std::move(block),
payload = std::move(payload)](td::Result<td::Unit> R) mutable {
R.ensure();
td::actor::send_closure(SelfId, &CatChainReceiverImpl::add_block_cont_3, std::move(block), std::move(payload));
});
db_.set(CatChainBlockHash::zero(), std::move(raw_data), std::move(P), 0);
}
void CatChainReceiverImpl::add_block_cont(tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload) {
validate_block_sync(block, payload.as_slice()).ensure();
if (opts_.debug_disable_db) {
add_block_cont_2(std::move(block), std::move(payload));
return;
}
auto id = CatChainReceivedBlock::block_hash(this, block, payload.as_slice());
auto raw_data = serialize_tl_object(block, true, payload.as_slice());
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), block = std::move(block),
payload = std::move(payload)](td::Result<td::Unit> R) mutable {
R.ensure();
td::actor::send_closure(SelfId, &CatChainReceiverImpl::add_block_cont_2, std::move(block), std::move(payload));
});
db_.set(id, std::move(raw_data), std::move(P), 0);
}
void CatChainReceiverImpl::add_block(td::BufferSlice payload, std::vector<CatChainBlockHash> deps) {
if (active_send_) {
auto B = std::make_unique<PendingBlock>(std::move(payload), std::move(deps));
pending_blocks_.push_back(std::move(B));
return;
}
active_send_ = true;
auto S = get_source_by_hash(local_id_);
CHECK(S != nullptr);
CHECK(S->get_id() == local_idx_);
if (!intentional_fork_) {
CHECK(!S->blamed());
}
auto prev = last_sent_block_->export_tl_dep();
std::vector<tl_object_ptr<ton_api::catchain_block_dep>> deps_arr;
deps_arr.resize(deps.size());
for (size_t i = 0; i < deps.size(); i++) {
auto B = get_block(deps[i]);
LOG_CHECK(B != nullptr) << this << ": cannot find block with hash " << deps[i];
if (!intentional_fork_) {
CHECK(B->get_source_id() != local_idx_);
}
deps_arr[i] = B->export_tl_dep();
}
auto height = prev->height_ + 1;
auto block_data = create_tl_object<ton_api::catchain_block_data>(std::move(prev), std::move(deps_arr));
auto block = create_tl_object<ton_api::catchain_block>(incarnation_, local_idx_, height, std::move(block_data),
td::BufferSlice());
auto id = CatChainReceivedBlock::block_id(this, block, payload);
auto id_s = serialize_tl_object(id, true);
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), print_id = print_id(), block = std::move(block),
payload = std::move(payload)](td::Result<td::BufferSlice> R) mutable {
if (R.is_error()) {
LOG(FATAL) << print_id << ": failed to sign: " << R.move_as_error();
return;
}
block->signature_ = R.move_as_ok();
td::actor::send_closure(SelfId, &CatChainReceiverImpl::add_block_cont, std::move(block), std::move(payload));
});
td::actor::send_closure_later(keyring_, &keyring::Keyring::sign_message, local_id_, std::move(id_s), std::move(P));
}
void CatChainReceiverImpl::debug_add_fork_cont(tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload) {
validate_block_sync(block, payload.as_slice()).ensure();
auto B = create_block(std::move(block), td::SharedSlice{payload.as_slice()});
B->written();
run_scheduler();
CHECK(B->delivered());
active_send_ = false;
if (pending_blocks_.size() > 0) {
auto B = std::move(pending_blocks_.front());
pending_blocks_.pop_front();
add_block(std::move(B->payload_), std::move(B->deps_));
}
}
void CatChainReceiverImpl::debug_add_fork(td::BufferSlice payload, CatChainBlockHeight height,
std::vector<CatChainBlockHash> deps) {
intentional_fork_ = true;
auto S = get_source_by_hash(local_id_);
CHECK(S != nullptr);
CHECK(S->get_id() == local_idx_);
if (height > S->received_height() + 1) {
height = S->received_height() + 1;
}
CHECK(height > 0);
CatChainReceivedBlock *prev;
if (height == 1) {
prev = root_block_;
} else {
prev = sources_[local_idx_]->get_block(height - 1);
CHECK(prev);
}
std::vector<tl_object_ptr<ton_api::catchain_block_dep>> deps_arr;
deps_arr.resize(deps.size());
for (size_t i = 0; i < deps.size(); i++) {
auto B = get_block(deps[i]);
LOG_CHECK(B != nullptr) << this << ": cannot find block with hash " << deps[i];
CHECK(B->get_source_id() != local_idx_);
deps_arr[i] = B->export_tl_dep();
}
auto block_data = create_tl_object<ton_api::catchain_block_data>(prev->export_tl_dep(), std::move(deps_arr));
auto block = create_tl_object<ton_api::catchain_block>(incarnation_, local_idx_, height, std::move(block_data),
td::BufferSlice());
auto id = CatChainReceivedBlock::block_id(this, block, payload);
auto id_s = serialize_tl_object(id, true);
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), print_id = print_id(), block = std::move(block),
payload = std::move(payload)](td::Result<td::BufferSlice> R) mutable {
if (R.is_error()) {
LOG(FATAL) << print_id << ": failed to sign: " << R.move_as_error();
return;
}
block->signature_ = R.move_as_ok();
td::actor::send_closure(SelfId, &CatChainReceiverImpl::debug_add_fork_cont, std::move(block), std::move(payload));
});
td::actor::send_closure_later(keyring_, &keyring::Keyring::sign_message, local_id_, std::move(id_s), std::move(P));
}
CatChainReceiverImpl::CatChainReceiverImpl(std::unique_ptr<Callback> callback, CatChainOptions opts,
td::actor::ActorId<keyring::Keyring> keyring,
td::actor::ActorId<adnl::Adnl> adnl,
td::actor::ActorId<overlay::Overlays> overlay_manager,
std::vector<CatChainNode> ids, PublicKeyHash local_id,
CatChainSessionId unique_hash, std::string db_root)
: callback_(std::move(callback))
, opts_(std::move(opts))
, keyring_(keyring)
, adnl_(adnl)
, overlay_manager_(overlay_manager)
, local_id_(local_id)
, db_root_(db_root) {
std::vector<td::Bits256> short_ids;
local_idx_ = static_cast<td::uint32>(ids.size());
for (auto &id : ids) {
td::uint32 seq = static_cast<td::uint32>(sources_.size());
auto R = CatChainReceiverSource::create(this, id.pub_key, id.adnl_id, seq);
auto S = R.move_as_ok();
auto h = id.pub_key.compute_short_id();
short_ids.push_back(h.bits256_value());
sources_hashes_[h] = seq;
sources_adnl_addrs_[id.adnl_id] = seq;
sources_.push_back(std::move(S));
if (h == local_id_) {
CHECK(local_idx_ == static_cast<td::uint32>(ids.size()));
local_idx_ = seq;
}
}
CHECK(local_idx_ != static_cast<td::uint32>(ids.size()));
//std::sort(short_ids.begin(), short_ids.end());
auto F = create_tl_object<ton_api::catchain_firstblock>(unique_hash, std::move(short_ids));
overlay_full_id_ = overlay::OverlayIdFull{serialize_tl_object(F, true)};
overlay_id_ = overlay_full_id_.compute_short_id();
incarnation_ = overlay_id_.bits256_value();
auto R = CatChainReceivedBlock::create_root(get_sources_cnt(), incarnation_, this);
root_block_ = R.get();
blocks_[root_block_->get_hash()] = std::move(R);
last_sent_block_ = root_block_;
choose_neighbours();
}
void CatChainReceiverImpl::start_up() {
std::vector<adnl::AdnlNodeIdShort> ids;
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
ids.push_back(get_source(i)->get_adnl_id());
}
std::map<PublicKeyHash, td::uint32> root_keys;
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
root_keys.emplace(get_source(i)->get_hash(), 16 << 20);
}
td::actor::send_closure(overlay_manager_, &overlay::Overlays::create_private_overlay,
get_source(local_idx_)->get_adnl_id(), overlay_full_id_.clone(), std::move(ids),
make_callback(), overlay::OverlayPrivacyRules{0, std::move(root_keys)});
CHECK(root_block_);
if (!opts_.debug_disable_db) {
std::shared_ptr<td::KeyValue> kv = std::make_shared<td::RocksDb>(
td::RocksDb::open(db_root_ + "/catchainreceiver-" + td::base64url_encode(as_slice(incarnation_))).move_as_ok());
db_ = DbType{std::move(kv)};
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<DbType::GetResult> R) {
R.ensure();
auto g = R.move_as_ok();
if (g.status == td::KeyValue::GetStatus::NotFound) {
td::actor::send_closure(SelfId, &CatChainReceiverImpl::read_db);
} else {
auto B = std::move(g.value);
CHECK(B.size() == 32);
CatChainBlockHash x;
as_slice(x).copy_from(B.as_slice());
td::actor::send_closure(SelfId, &CatChainReceiverImpl::read_db_from, x);
}
});
db_.get(CatChainBlockHash::zero(), std::move(P));
} else {
read_db();
}
}
void CatChainReceiverImpl::tear_down() {
td::actor::send_closure(overlay_manager_, &overlay::Overlays::delete_overlay, get_source(local_idx_)->get_adnl_id(),
overlay_id_);
}
void CatChainReceiverImpl::read_db_from(CatChainBlockHash id) {
pending_in_db_ = 1;
db_root_block_ = id;
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id](td::Result<DbType::GetResult> R) {
R.ensure();
auto g = R.move_as_ok();
CHECK(g.status == td::KeyValue::GetStatus::Ok);
td::actor::send_closure(SelfId, &CatChainReceiverImpl::read_block_from_db, id, std::move(g.value));
});
db_.get(id, std::move(P));
}
void CatChainReceiverImpl::read_block_from_db(CatChainBlockHash id, td::BufferSlice data) {
pending_in_db_--;
auto F = fetch_tl_prefix<ton_api::catchain_block>(data, true);
F.ensure();
auto block = F.move_as_ok();
auto payload = std::move(data);
auto block_id = CatChainReceivedBlock::block_hash(this, block, payload);
CHECK(block_id == id);
auto B = get_block(id);
if (B && B->initialized()) {
CHECK(B->in_db());
if (!pending_in_db_) {
read_db();
}
return;
}
auto source = get_source(block->src_);
CHECK(source != nullptr);
CHECK(block->incarnation_ == incarnation_);
validate_block_sync(block, payload).ensure();
B = create_block(std::move(block), td::SharedSlice{payload.as_slice()});
B->written();
CHECK(B);
auto deps = B->get_dep_hashes();
deps.push_back(B->get_prev_hash());
for (auto &dep : deps) {
auto dep_block = get_block(dep);
if (!dep_block || !dep_block->initialized()) {
pending_in_db_++;
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), dep](td::Result<DbType::GetResult> R) {
R.ensure();
auto g = R.move_as_ok();
CHECK(g.status == td::KeyValue::GetStatus::Ok);
td::actor::send_closure(SelfId, &CatChainReceiverImpl::read_block_from_db, dep, std::move(g.value));
});
db_.get(dep, std::move(P));
}
}
if (!pending_in_db_) {
read_db();
}
}
void CatChainReceiverImpl::read_db() {
if (!db_root_block_.is_zero()) {
run_scheduler();
last_sent_block_ = get_block(db_root_block_);
CHECK(last_sent_block_);
CHECK(last_sent_block_->delivered());
}
read_db_ = true;
next_rotate_ = td::Timestamp::in(60 + td::Random::fast(0, 60));
next_sync_ = td::Timestamp::in(0.01 * td::Random::fast(0, 60));
alarm_timestamp().relax(next_rotate_);
alarm_timestamp().relax(next_sync_);
callback_->start();
}
td::actor::ActorOwn<CatChainReceiverInterface> CatChainReceiverInterface::create(
std::unique_ptr<Callback> callback, CatChainOptions opts, td::actor::ActorId<keyring::Keyring> keyring,
td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<overlay::Overlays> overlay_manager,
std::vector<CatChainNode> ids, PublicKeyHash local_id, CatChainSessionId unique_hash, std::string db_root) {
auto A = td::actor::create_actor<CatChainReceiverImpl>("catchainreceiver", std::move(callback), std::move(opts),
keyring, adnl, overlay_manager, std::move(ids), local_id,
unique_hash, db_root);
return std::move(A);
}
CatChainReceiverSource *CatChainReceiverImpl::get_source_by_hash(PublicKeyHash source_hash) const {
auto it = sources_hashes_.find(source_hash);
if (it == sources_hashes_.end()) {
return nullptr;
}
return get_source(it->second);
}
CatChainReceiverSource *CatChainReceiverImpl::get_source_by_adnl_id(adnl::AdnlNodeIdShort source_hash) const {
auto it = sources_adnl_addrs_.find(source_hash);
if (it == sources_adnl_addrs_.end()) {
return nullptr;
}
return get_source(it->second);
}
void CatChainReceiverImpl::receive_query_from_overlay(adnl::AdnlNodeIdShort src, td::BufferSlice data,
td::Promise<td::BufferSlice> promise) {
if (!read_db_) {
promise.set_error(td::Status::Error(ErrorCode::notready, "db not read"));
return;
}
td::PerfWarningTimer t{"catchain query process", 0.001};
auto F = fetch_tl_object<ton_api::Function>(data.clone(), true);
if (F.is_error()) {
callback_->on_custom_query(get_source_by_adnl_id(src)->get_hash(), std::move(data), std::move(promise));
//LOG(WARNING) << this << ": unknown query from " << src;
return;
}
auto f = F.move_as_ok();
ton_api::downcast_call(*f.get(), [&](auto &obj) { this->process_query(src, obj, std::move(promise)); });
}
void CatChainReceiverImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlock &query,
td::Promise<td::BufferSlice> promise) {
auto it = blocks_.find(query.block_);
if (it == blocks_.end() || it->second->get_height() == 0 || !it->second->initialized()) {
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_blockNotFound>(), true));
} else {
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_blockResult>(it->second->export_tl()),
true, it->second->get_payload().as_slice()));
}
}
void CatChainReceiverImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlocks &query,
td::Promise<td::BufferSlice> promise) {
if (query.blocks_.size() > 100) {
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "too many blocks"));
return;
}
td::int32 cnt = 0;
for (auto &b : query.blocks_) {
auto it = blocks_.find(b);
if (it != blocks_.end() && it->second->get_height() > 0) {
auto block = create_tl_object<ton_api::catchain_blockUpdate>(it->second->export_tl());
CHECK(it->second->get_payload().size() > 0);
auto B = serialize_tl_object(block, true, it->second->get_payload().clone());
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_message, src,
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(B));
cnt++;
}
}
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_sent>(cnt), true));
}
void CatChainReceiverImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlockHistory &query,
td::Promise<td::BufferSlice> promise) {
auto h = query.height_;
if (h <= 0) {
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "not-positive height"));
return;
}
if (h > 100) {
h = 100;
}
std::set<CatChainBlockHash> s{query.stop_if_.begin(), query.stop_if_.end()};
auto B = get_block(query.block_);
if (B == nullptr) {
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_sent>(0), true));
return;
}
if (static_cast<CatChainBlockHeight>(h) > B->get_height()) {
h = B->get_height();
}
td::uint32 cnt = 0;
while (h-- > 0) {
if (s.find(B->get_hash()) != s.end()) {
break;
}
auto block = create_tl_object<ton_api::catchain_blockUpdate>(B->export_tl());
CHECK(B->get_payload().size() > 0);
auto BB = serialize_tl_object(block, true, B->get_payload().as_slice());
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_message, src,
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(BB));
B = B->get_prev();
cnt++;
}
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_sent>(cnt), true));
}
void CatChainReceiverImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getDifference &query,
td::Promise<td::BufferSlice> promise) {
auto &vt = query.rt_;
if (vt.size() != get_sources_cnt()) {
VLOG(CATCHAIN_WARNING) << this << ": incorrect query from " << src;
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "bad vt size"));
return;
}
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
if (vt[i] >= 0) {
auto S = get_source(i);
if (S->fork_is_found()) {
auto obj = fetch_tl_object<ton_api::catchain_block_data_fork>(S->fork_proof(), true);
obj.ensure();
auto f = obj.move_as_ok();
promise.set_value(
create_serialize_tl_object<ton_api::catchain_differenceFork>(std::move(f->left_), std::move(f->right_)));
return;
}
}
}
std::vector<td::int32> my_vt(get_sources_cnt());
td::uint64 total = 0;
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
if (vt[i] >= 0) {
auto x = static_cast<CatChainBlockHeight>(vt[i]);
auto S = get_source(i);
if (S->delivered_height() > x) {
total += S->delivered_height() - x;
}
my_vt[i] = S->delivered_height();
} else {
my_vt[i] = -1;
}
}
const td::uint32 max_send = 100;
td::int32 l = 0;
td::int32 r = max_send + 1;
while (r - l > 1) {
td::int32 x = (r + l) / 2;
td::uint64 sum = 0;
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
if (vt[i] >= 0 && my_vt[i] > vt[i]) {
sum += (my_vt[i] - vt[i] > x) ? x : (my_vt[i] - vt[i]);
}
}
if (sum > max_send) {
r = x;
} else {
l = x;
}
}
CHECK(r > 0);
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
if (vt[i] >= 0 && my_vt[i] > vt[i]) {
auto S = get_source(i);
auto t = (my_vt[i] - vt[i] > r) ? r : (my_vt[i] - vt[i]);
CHECK(t > 0);
while (t-- > 0) {
auto M = S->get_block(++vt[i]);
CHECK(M != nullptr);
auto block = create_tl_object<ton_api::catchain_blockUpdate>(M->export_tl());
CHECK(M->get_payload().size() > 0);
auto BB = serialize_tl_object(block, true, M->get_payload().as_slice());
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_message, src,
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(BB));
}
}
}
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_difference>(std::move(vt)), true));
}
void CatChainReceiverImpl::got_fork_proof(td::BufferSlice data) {
auto F = fetch_tl_object<ton_api::catchain_differenceFork>(std::move(data), true);
if (F.is_error()) {
VLOG(CATCHAIN_WARNING) << this << ": received bad fork proof: " << F.move_as_error();
return;
}
auto f = F.move_as_ok();
{
td::Status S;
S = validate_block_sync(f->left_);
if (S.is_error()) {
VLOG(CATCHAIN_WARNING) << this << ": incorrect fork blame: left is invalid: " << S.move_as_error();
return;
}
S = validate_block_sync(f->right_);
if (S.is_error()) {
VLOG(CATCHAIN_WARNING) << this << ": incorrect fork blame: right is invalid: " << S.move_as_error();
return;
}
}
// block is incorrect, since blocks are
if (f->left_->height_ != f->right_->height_ || f->left_->src_ != f->right_->src_ ||
f->left_->data_hash_ == f->right_->data_hash_) {
VLOG(CATCHAIN_WARNING) << this << ": incorrect fork blame: not a fork";
return;
}
auto S = get_source(f->left_->src_);
S->on_found_fork_proof(
create_serialize_tl_object<ton_api::catchain_block_data_fork>(std::move(f->left_), std::move(f->right_)));
S->blame();
}
void CatChainReceiverImpl::synchronize_with(CatChainReceiverSource *S) {
CHECK(!S->blamed());
std::vector<td::int32> rt(get_sources_cnt());
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
auto SS = get_source(i);
if (SS->blamed()) {
rt[i] = -1;
} else {
rt[i] = S->delivered_height();
}
}
auto P = td::PromiseCreator::lambda(
[SelfId = actor_id(this), src = S->get_hash(), print_id = print_id()](td::Result<td::BufferSlice> R) {
if (R.is_error()) {
VLOG(CATCHAIN_INFO) << print_id << ": timedout syncronize query to " << src;
return;
}
auto data = R.move_as_ok();
auto X = fetch_tl_object<ton_api::catchain_Difference>(data.clone(), true);
if (X.is_error()) {
VLOG(CATCHAIN_WARNING) << print_id << ": received incorrect answer to syncronize query from " << src << ": "
<< X.move_as_error();
return;
}
auto A = X.move_as_ok();
if (A->get_id() == ton_api::catchain_differenceFork::ID) {
td::actor::send_closure(SelfId, &CatChainReceiverImpl::got_fork_proof, std::move(data));
}
// use answer ?
return;
});
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_query, S->get_adnl_id(),
get_source(local_idx_)->get_adnl_id(), overlay_id_, "sync", std::move(P),
td::Timestamp::in(5.0),
serialize_tl_object(create_tl_object<ton_api::catchain_getDifference>(std::move(rt)), true));
if (S->delivered_height() < S->received_height()) {
auto B = S->get_block(S->delivered_height() + 1);
CHECK(B->initialized());
std::vector<CatChainBlockHash> vec;
B->find_pending_deps(vec, 16);
for (auto &hash : vec) {
auto P = td::PromiseCreator::lambda(
[SelfId = actor_id(this), print_id = print_id(), src = S->get_adnl_id()](td::Result<td::BufferSlice> R) {
if (R.is_error()) {
VLOG(CATCHAIN_INFO) << print_id << ": timedout syncronize query to " << src;
} else {
td::actor::send_closure(SelfId, &CatChainReceiverImpl::receive_block_answer, src, R.move_as_ok());
}
});
auto query = serialize_tl_object(create_tl_object<ton_api::catchain_getBlock>(hash), true);
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_query, S->get_adnl_id(),
get_source(local_idx_)->get_adnl_id(), overlay_id_, "sync blocks", std::move(P),
td::Timestamp::in(2.0), std::move(query));
}
}
}
void CatChainReceiverImpl::choose_neighbours() {
std::vector<td::uint32> n;
n.resize(get_max_neighbours());
td::uint32 size = 0;
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
if (i == local_idx_) {
continue;
}
auto S = get_source(i);
if (!S->blamed()) {
size++;
if (size <= n.size()) {
n[size - 1] = i;
} else {
td::uint32 id = td::Random::fast(0, size - 1);
if (id < n.size()) {
n[id] = i;
}
}
}
}
if (size < n.size()) {
n.resize(size);
}
neighbours_ = std::move(n);
}
void CatChainReceiverImpl::alarm() {
alarm_timestamp() = td::Timestamp::never();
if (next_sync_ && next_sync_.is_in_past()) {
next_sync_ = td::Timestamp::in(td::Random::fast(2.0, 3.0));
for (auto i = 0; i < 3; i++) {
auto S = get_source(td::Random::fast(0, get_sources_cnt() - 1));
CHECK(S != nullptr);
if (!S->blamed()) {
synchronize_with(S);
break;
}
}
}
if (next_rotate_ && next_rotate_.is_in_past()) {
next_rotate_ = td::Timestamp::in(td::Random::fast(60.0, 120.0));
choose_neighbours();
}
alarm_timestamp().relax(next_rotate_);
alarm_timestamp().relax(next_sync_);
}
void CatChainReceiverImpl::send_fec_broadcast(td::BufferSlice data) {
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_broadcast_fec_ex,
get_source(local_idx_)->get_adnl_id(), overlay_id_, local_id_, 0, std::move(data));
}
void CatChainReceiverImpl::send_custom_query_data(PublicKeyHash dst, std::string name,
td::Promise<td::BufferSlice> promise, td::Timestamp timeout,
td::BufferSlice query) {
auto S = get_source_by_hash(dst);
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_query, S->get_adnl_id(),
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(name), std::move(promise),
timeout, std::move(query));
}
void CatChainReceiverImpl::send_custom_query_data_via(PublicKeyHash dst, std::string name,
td::Promise<td::BufferSlice> promise, td::Timestamp timeout,
td::BufferSlice query, td::uint64 max_answer_size,
td::actor::ActorId<adnl::AdnlSenderInterface> via) {
auto S = get_source_by_hash(dst);
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_query_via, S->get_adnl_id(),
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(name), std::move(promise),
timeout, std::move(query), max_answer_size, via);
}
void CatChainReceiverImpl::send_custom_message_data(PublicKeyHash dst, td::BufferSlice data) {
auto S = get_source_by_hash(dst);
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_message, S->get_adnl_id(),
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(data));
}
void CatChainReceiverImpl::block_written_to_db(CatChainBlockHash hash) {
auto block = get_block(hash);
CHECK(block);
block->written();
run_scheduler();
}
static void destroy_db(std::string name, td::uint32 attempt) {
auto S = td::RocksDb::destroy(name);
if (S.is_ok()) {
return;
}
if (S.is_error() && attempt >= 10) {
LOG(ERROR) << "failed to destroy catchain " << name << ": " << S;
} else {
LOG(DEBUG) << "failed to destroy catchain " << name << ": " << S;
delay_action([name, attempt]() { destroy_db(name, attempt); }, td::Timestamp::in(1.0));
}
}
void CatChainReceiverImpl::destroy() {
auto name = db_root_ + "/catchainreceiver-" + td::base64url_encode(as_slice(incarnation_));
delay_action([name]() { destroy_db(name, 0); }, td::Timestamp::in(1.0));
stop();
}
} // namespace catchain
} // namespace ton

View file

@ -0,0 +1,89 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "adnl/adnl.h"
#include "adnl/utils.hpp"
#include "auto/tl/ton_api.h"
#include "catchain-types.h"
#include "catchain-receiver-interface.h"
namespace ton {
namespace catchain {
constexpr int VERBOSITY_NAME(CATCHAIN_WARNING) = verbosity_WARNING;
constexpr int VERBOSITY_NAME(CATCHAIN_NOTICE) = verbosity_DEBUG;
constexpr int VERBOSITY_NAME(CATCHAIN_INFO) = verbosity_DEBUG;
constexpr int VERBOSITY_NAME(CATCHAIN_DEBUG) = verbosity_DEBUG;
constexpr int VERBOSITY_NAME(CATCHAIN_EXTRA_DEBUG) = verbosity_DEBUG + 1;
class CatChainReceivedBlock;
class CatChainReceiverSource;
class CatChainReceiver : public CatChainReceiverInterface {
public:
struct PrintId {
CatChainSessionId instance_;
PublicKeyHash local_id_;
};
td::uint32 get_max_neighbours() const {
return 5;
}
virtual PrintId print_id() const = 0;
virtual CatChainReceivedBlock *create_block(tl_object_ptr<ton_api::catchain_block> block,
td::SharedSlice payload) = 0;
virtual CatChainReceivedBlock *create_block(tl_object_ptr<ton_api::catchain_block_dep> block) = 0;
virtual CatChainReceiverSource *get_source(td::uint32 source_id) const = 0;
virtual PublicKeyHash get_source_hash(td::uint32 source_id) const = 0;
virtual td::uint32 get_forks_cnt() const = 0;
virtual td::uint32 get_sources_cnt() const = 0;
virtual CatChainSessionId get_incarnation() const = 0;
virtual void run_block(CatChainReceivedBlock *block) = 0;
virtual void deliver_block(CatChainReceivedBlock *block) = 0;
virtual td::uint32 add_fork() = 0;
virtual void add_prepared_event(td::BufferSlice data) = 0;
virtual void on_blame(td::uint32 source_id) = 0;
virtual const CatChainOptions &opts() const = 0;
virtual td::Status validate_block_sync(tl_object_ptr<ton_api::catchain_block_dep> &dep) = 0;
virtual td::Status validate_block_sync(tl_object_ptr<ton_api::catchain_block> &block, td::Slice payload) = 0;
virtual ~CatChainReceiver() = default;
};
} // namespace catchain
} // namespace ton
namespace td {
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceiver::PrintId &print_id) {
sb << "[catchainreceiver " << print_id.instance_ << "@" << print_id.local_id_ << "]";
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceiver *catchain) {
sb << catchain->print_id();
return sb;
}
} // namespace td

View file

@ -0,0 +1,240 @@
/*
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 <list>
#include <queue>
#include <map>
#include "catchain-types.h"
#include "catchain-receiver.h"
#include "catchain-receiver-source.h"
#include "catchain-received-block.h"
#include "td/db/KeyValueAsync.h"
namespace ton {
namespace catchain {
class CatChainReceiverImpl : public CatChainReceiver {
public:
PrintId print_id() const override {
return PrintId{incarnation_, local_id_};
}
void add_prepared_event(td::BufferSlice data) override {
add_block(std::move(data), std::vector<CatChainBlockHash>());
}
CatChainSessionId get_incarnation() const override {
return incarnation_;
}
void run_block(CatChainReceivedBlock *block) override;
td::uint32 get_forks_cnt() const override {
return total_forks_;
}
td::uint32 get_sources_cnt() const override {
return static_cast<td::uint32>(sources_.size());
}
CatChainReceiverSource *get_source(td::uint32 source_id) const override {
if (source_id >= get_sources_cnt()) {
return nullptr;
}
return sources_[source_id].get();
}
PublicKeyHash get_source_hash(td::uint32 source_id) const override;
CatChainReceiverSource *get_source_by_hash(PublicKeyHash source_hash) const;
CatChainReceiverSource *get_source_by_adnl_id(adnl::AdnlNodeIdShort source_hash) const;
td::uint32 add_fork() override;
void deliver_block(CatChainReceivedBlock *block) override;
void receive_message_from_overlay(adnl::AdnlNodeIdShort src, td::BufferSlice data);
void receive_query_from_overlay(adnl::AdnlNodeIdShort src, td::BufferSlice data,
td::Promise<td::BufferSlice> promise);
void process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlock &query,
td::Promise<td::BufferSlice> promise);
void process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlocks &query,
td::Promise<td::BufferSlice> promise);
void process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlockHistory &query,
td::Promise<td::BufferSlice> promise);
void process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getDifference &query,
td::Promise<td::BufferSlice> promise);
template <class T>
void process_query(adnl::AdnlNodeIdShort src, T &query, td::Promise<td::BufferSlice> promise) {
//LOG(WARNING) << this << ": unknown query from " << src;
callback_->on_custom_query(get_source_by_adnl_id(src)->get_hash(), serialize_tl_object(&query, true),
std::move(promise));
}
void receive_broadcast_from_overlay(PublicKeyHash src, td::BufferSlice data);
void receive_block(adnl::AdnlNodeIdShort src, tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload);
void receive_block_answer(adnl::AdnlNodeIdShort src, td::BufferSlice);
//void send_block(PublicKeyHash src, tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload);
CatChainReceivedBlock *create_block(tl_object_ptr<ton_api::catchain_block> block, td::SharedSlice payload) override;
CatChainReceivedBlock *create_block(tl_object_ptr<ton_api::catchain_block_dep> block) override;
td::Status validate_block_sync(tl_object_ptr<ton_api::catchain_block_dep> &dep) override;
td::Status validate_block_sync(tl_object_ptr<ton_api::catchain_block> &block, td::Slice payload) override;
void send_fec_broadcast(td::BufferSlice data) override;
void send_custom_query_data(PublicKeyHash dst, std::string name, td::Promise<td::BufferSlice> promise,
td::Timestamp timeout, td::BufferSlice query) override;
void send_custom_query_data_via(PublicKeyHash dst, std::string name, td::Promise<td::BufferSlice> promise,
td::Timestamp timeout, td::BufferSlice query, td::uint64 max_answer_size,
td::actor::ActorId<adnl::AdnlSenderInterface> via) override;
void send_custom_message_data(PublicKeyHash dst, td::BufferSlice query) override;
void run_scheduler();
void add_block(td::BufferSlice data, std::vector<CatChainBlockHash> deps) override;
void add_block_cont(tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload);
void add_block_cont_2(tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload);
void add_block_cont_3(tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload);
void debug_add_fork(td::BufferSlice payload, CatChainBlockHeight height,
std::vector<CatChainBlockHash> deps) override;
void debug_add_fork_cont(tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload);
void on_blame(td::uint32 src) override {
callback_->blame(src);
}
void blame_node(td::uint32 idx) override {
}
const CatChainOptions &opts() const override {
return opts_;
}
void got_fork_proof(td::BufferSlice data);
void synchronize_with(CatChainReceiverSource *source);
void alarm() override;
void start_up() override;
void tear_down() override;
void read_db();
void read_db_from(CatChainBlockHash id);
void read_block_from_db(CatChainBlockHash id, td::BufferSlice data);
void block_written_to_db(CatChainBlockHash hash);
void destroy() override;
CatChainReceivedBlock *get_block(CatChainBlockHash hash) const;
CatChainReceiverImpl(std::unique_ptr<Callback> callback, CatChainOptions opts,
td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<adnl::Adnl> adnl,
td::actor::ActorId<overlay::Overlays>, std::vector<CatChainNode> ids, PublicKeyHash local_id,
CatChainBlockHash unique_hash, std::string db_root);
private:
std::unique_ptr<overlay::Overlays::Callback> make_callback() {
class Callback : public overlay::Overlays::Callback {
public:
void receive_message(adnl::AdnlNodeIdShort src, overlay::OverlayIdShort overlay_id,
td::BufferSlice data) override {
td::actor::send_closure(id_, &CatChainReceiverImpl::receive_message_from_overlay, src, std::move(data));
}
void receive_query(adnl::AdnlNodeIdShort src, overlay::OverlayIdShort overlay_id, td::BufferSlice data,
td::Promise<td::BufferSlice> promise) override {
td::actor::send_closure(id_, &CatChainReceiverImpl::receive_query_from_overlay, src, std::move(data),
std::move(promise));
}
void receive_broadcast(PublicKeyHash src, overlay::OverlayIdShort overlay_id, td::BufferSlice data) override {
td::actor::send_closure(id_, &CatChainReceiverImpl::receive_broadcast_from_overlay, src, std::move(data));
}
Callback(td::actor::ActorId<CatChainReceiverImpl> id) : id_(std::move(id)) {
}
private:
td::actor::ActorId<CatChainReceiverImpl> id_;
};
return std::make_unique<Callback>(actor_id(this));
}
struct PendingBlock {
td::BufferSlice payload_;
std::vector<CatChainBlockHash> deps_;
PendingBlock(td::BufferSlice &&payload, std::vector<CatChainBlockHash> &&deps)
: payload_(std::move(payload)), deps_(std::move(deps)) {
}
};
std::list<std::unique_ptr<PendingBlock>> pending_blocks_;
bool active_send_ = false;
bool read_db_ = false;
td::uint32 pending_in_db_ = 0;
CatChainBlockHash db_root_block_ = CatChainBlockHash::zero();
void choose_neighbours();
std::vector<std::unique_ptr<CatChainReceiverSource>> sources_;
std::map<PublicKeyHash, td::uint32> sources_hashes_;
std::map<adnl::AdnlNodeIdShort, td::uint32> sources_adnl_addrs_;
td::uint32 total_forks_ = 0;
std::map<CatChainBlockHash, std::unique_ptr<CatChainReceivedBlock>> blocks_;
CatChainReceivedBlock *root_block_;
CatChainReceivedBlock *last_sent_block_;
CatChainSessionId incarnation_;
std::unique_ptr<Callback> callback_;
CatChainOptions opts_;
std::vector<td::uint32> neighbours_;
//std::queue<tl_object_ptr<ton_api::catchain_block_inner_Data>> events_;
//std::queue<td::BufferSlice> raw_events_;
td::actor::ActorId<keyring::Keyring> keyring_;
td::actor::ActorId<adnl::Adnl> adnl_;
td::actor::ActorId<overlay::Overlays> overlay_manager_;
overlay::OverlayIdShort overlay_id_;
overlay::OverlayIdFull overlay_full_id_;
PublicKeyHash local_id_;
td::uint32 local_idx_;
td::Timestamp next_sync_;
td::Timestamp next_rotate_;
std::string db_root_;
using DbType = td::KeyValueAsync<CatChainBlockHash, td::BufferSlice>;
DbType db_;
bool intentional_fork_ = false;
std::list<CatChainReceivedBlock *> to_run_;
};
} // namespace catchain
} // namespace ton
namespace td {
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceiverImpl *catchain) {
sb << catchain->print_id();
return sb;
}
} // namespace td

47
catchain/catchain-types.h Normal file
View file

@ -0,0 +1,47 @@
/*
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 "adnl/adnl-node-id.hpp"
namespace ton {
namespace catchain {
using CatChainBlockHash = td::Bits256;
using CatChainBlockPayloadHash = td::Bits256;
using CatChainBlockHeight = td::uint32;
using CatChainSessionId = td::Bits256;
struct CatChainNode {
adnl::AdnlNodeIdShort adnl_id;
PublicKey pub_key;
};
struct CatChainOptions {
td::Clocks::Duration idle_timeout = 16.0;
td::uint32 max_deps = 4;
bool debug_disable_db = false;
};
} // namespace catchain
} // namespace ton

317
catchain/catchain.cpp Normal file
View file

@ -0,0 +1,317 @@
/*
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 "catchain-types.h"
#include "catchain.hpp"
#include "catchain-receiver.h"
#include "adnl/utils.hpp"
namespace ton {
namespace catchain {
void CatChainImpl::send_process() {
CHECK(receiver_started_);
std::vector<CatChainBlock *> v;
std::vector<CatChainBlockHash> w;
while (top_blocks_.size() > 0 && v.size() < opts_.max_deps) {
auto B = *top_blocks_.get_random();
CHECK(B != nullptr);
top_blocks_.remove(B->hash());
if (B->source() == sources_.size() || !blamed_sources_[B->source()]) {
w.push_back(B->hash());
v.push_back(B);
set_processed(B);
}
}
process_deps_ = std::move(w);
VLOG(CATCHAIN_INFO) << this << ": creating block. deps=" << process_deps_;
callback_->process_blocks(std::move(v));
VLOG(CATCHAIN_INFO) << this << ": sent creating block";
}
void CatChainImpl::send_preprocess(CatChainBlock *block) {
if (block->preprocess_is_sent()) {
return;
}
auto prev = block->prev();
if (prev) {
send_preprocess(prev);
}
auto deps = block->deps();
for (auto X : deps) {
send_preprocess(X);
}
block->preprocess_sent();
VLOG(CATCHAIN_INFO) << this << ": preprocessing block " << block->hash() << " src=" << block->source();
callback_->preprocess_block(block);
VLOG(CATCHAIN_INFO) << this << ": sent preprocessing block " << block->hash() << " src=" << block->source();
}
void CatChainImpl::set_processed(CatChainBlock *block) {
if (block->is_processed()) {
return;
}
auto prev = block->prev();
if (prev) {
set_processed(prev);
}
auto deps = block->deps();
for (auto X : deps) {
set_processed(X);
}
block->set_processed();
}
void CatChainImpl::processed_block(td::BufferSlice payload) {
CHECK(receiver_started_);
VLOG(CATCHAIN_INFO) << this << ": created block. deps=" << process_deps_ << " payload_size=" << payload.size();
td::actor::send_closure(receiver_, &CatChainReceiverInterface::add_block, std::move(payload),
std::move(process_deps_));
CHECK(active_process_);
if (top_blocks_.size() > 0 || force_process_) {
force_process_ = false;
send_process();
} else {
active_process_ = false;
VLOG(CATCHAIN_INFO) << this << ": finished processing";
callback_->finished_processing();
VLOG(CATCHAIN_INFO) << this << ": sent finished processing";
alarm_timestamp() = td::Timestamp::in(opts_.idle_timeout);
}
}
void CatChainImpl::need_new_block(td::Timestamp t) {
if (!receiver_started_) {
return;
}
if (!force_process_) {
VLOG(CATCHAIN_INFO) << this << ": forcing creation of new block";
}
force_process_ = true;
if (!active_process_) {
alarm_timestamp().relax(t);
}
}
void CatChainImpl::on_new_block(td::uint32 src_id, td::uint32 fork, CatChainBlockHash hash, CatChainBlockHeight height,
CatChainBlockHash prev, std::vector<CatChainBlockHash> deps,
std::vector<CatChainBlockHeight> vt, td::SharedSlice data) {
VLOG(CATCHAIN_DEBUG) << this << ": new block " << hash;
if (top_blocks_.size() == 0 && !active_process_ && receiver_started_) {
alarm_timestamp().relax(td::Timestamp::in(opts_.idle_timeout));
}
CatChainBlock *p = nullptr;
if (!prev.is_zero()) {
p = get_block(prev);
CHECK(p != nullptr);
if (top_blocks_.exists(prev)) {
top_blocks_.remove(prev);
}
}
std::vector<CatChainBlock *> v;
v.resize(deps.size());
for (size_t i = 0; i < deps.size(); i++) {
if (!blamed_sources_[src_id] && top_blocks_.exists(deps[i])) {
top_blocks_.remove(deps[i]);
}
v[i] = get_block(deps[i]);
CHECK(v[i] != nullptr);
}
CHECK(src_id < sources_.size());
auto src_hash = sources_[src_id];
blocks_[hash] =
CatChainBlock::create(src_id, fork, src_hash, height, hash, std::move(data), p, std::move(v), std::move(vt));
auto B = get_block(hash);
CHECK(B != nullptr);
if (!blamed_sources_[src_id]) {
send_preprocess(B);
top_source_blocks_[src_id] = B;
if (src_id != local_idx_) {
top_blocks_.insert(B->hash(), B);
}
if (top_blocks_.size() == 0 && !active_process_ && receiver_started_) {
alarm_timestamp().relax(td::Timestamp::in(opts_.idle_timeout));
}
}
}
void CatChainImpl::on_blame(td::uint32 src_id) {
if (blamed_sources_[src_id]) {
return;
}
blamed_sources_[src_id] = true;
top_source_blocks_[src_id] = nullptr;
// recompute top blocks
top_blocks_.reset();
auto size = static_cast<td::uint32>(sources_.size());
for (td::uint32 i = 0; i < size; i++) {
if (!blamed_sources_[i] && top_source_blocks_[i] && i != local_idx_) {
auto B = top_source_blocks_[i];
bool f = true;
if (B->is_processed()) {
continue;
}
for (td::uint32 j = 0; j < size; j++) {
if (i != j && !blamed_sources_[j] && top_source_blocks_[j]) {
if (top_source_blocks_[j]->is_descendant_of(B)) {
f = false;
break;
}
}
}
if (f) {
top_blocks_.insert(B->hash(), B);
}
}
}
}
void CatChainImpl::on_custom_message(PublicKeyHash src, td::BufferSlice data) {
callback_->process_message(src, std::move(data));
}
void CatChainImpl::on_custom_query(PublicKeyHash src, td::BufferSlice data, td::Promise<td::BufferSlice> promise) {
callback_->process_query(src, std::move(data), std::move(promise));
}
void CatChainImpl::on_broadcast(PublicKeyHash src, td::BufferSlice data) {
VLOG(CATCHAIN_INFO) << this << ": processing broadcast";
callback_->process_broadcast(src, std::move(data));
VLOG(CATCHAIN_INFO) << this << ": sent processing broadcast";
}
void CatChainImpl::on_receiver_started() {
receiver_started_ = true;
callback_->started();
CHECK(!active_process_);
active_process_ = true;
send_process();
}
CatChainImpl::CatChainImpl(std::unique_ptr<Callback> callback, CatChainOptions opts,
td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<adnl::Adnl> adnl,
td::actor::ActorId<overlay::Overlays> overlay_manager, std::vector<CatChainNode> ids,
PublicKeyHash local_id, CatChainSessionId unique_hash, std::string db_root)
: opts_(std::move(opts)), db_root_(db_root) {
callback_ = std::move(callback);
sources_.resize(ids.size());
unique_hash_ = unique_hash;
for (size_t i = 0; i < ids.size(); i++) {
sources_[i] = ids[i].pub_key.compute_short_id();
if (sources_[i] == local_id) {
local_idx_ = static_cast<td::uint32>(i);
}
}
blamed_sources_.resize(ids.size(), false);
top_source_blocks_.resize(ids.size(), nullptr);
args_ = std::make_unique<Args>(keyring, adnl, overlay_manager, std::move(ids), local_id, unique_hash);
}
void CatChainImpl::alarm() {
alarm_timestamp() = td::Timestamp::never();
if (!active_process_) {
active_process_ = true;
send_process();
}
}
void CatChainImpl::start_up() {
class ChainCb : public CatChainReceiverInterface::Callback {
public:
void new_block(td::uint32 src_id, td::uint32 fork_id, CatChainBlockHash hash, CatChainBlockHeight height,
CatChainBlockHash prev, std::vector<CatChainBlockHash> deps, std::vector<CatChainBlockHeight> vt,
td::SharedSlice data) override {
td::actor::send_closure(id_, &CatChainImpl::on_new_block, src_id, fork_id, hash, height, prev, std::move(deps),
std::move(vt), std::move(data));
}
void blame(td::uint32 src_id) override {
td::actor::send_closure(id_, &CatChainImpl::on_blame, src_id);
}
void on_custom_message(PublicKeyHash src, td::BufferSlice data) override {
td::actor::send_closure(id_, &CatChainImpl::on_custom_message, src, std::move(data));
}
void on_custom_query(PublicKeyHash src, td::BufferSlice data, td::Promise<td::BufferSlice> promise) override {
td::actor::send_closure(id_, &CatChainImpl::on_custom_query, src, std::move(data), std::move(promise));
}
void on_broadcast(PublicKeyHash src, td::BufferSlice data) override {
td::actor::send_closure(id_, &CatChainImpl::on_broadcast, src, std::move(data));
}
void start() override {
td::actor::send_closure(id_, &CatChainImpl::on_receiver_started);
}
ChainCb(td::actor::ActorId<CatChainImpl> id) : id_(id) {
}
private:
td::actor::ActorId<CatChainImpl> id_;
};
auto cb = std::make_unique<ChainCb>(actor_id(this));
receiver_ =
CatChainReceiverInterface::create(std::move(cb), opts_, args_->keyring, args_->adnl, args_->overlay_manager,
std::move(args_->ids), args_->local_id, args_->unique_hash, db_root_);
args_ = nullptr;
//alarm_timestamp() = td::Timestamp::in(opts_.idle_timeout);
}
td::actor::ActorOwn<CatChain> CatChain::create(std::unique_ptr<Callback> callback, CatChainOptions opts,
td::actor::ActorId<keyring::Keyring> keyring,
td::actor::ActorId<adnl::Adnl> adnl,
td::actor::ActorId<overlay::Overlays> overlay_manager,
std::vector<CatChainNode> ids, PublicKeyHash local_id,
CatChainSessionId unique_hash, std::string db_root) {
return td::actor::create_actor<CatChainImpl>("catchain", std::move(callback), std::move(opts), keyring, adnl,
overlay_manager, std::move(ids), local_id, unique_hash, db_root);
}
CatChainBlock *CatChainImpl::get_block(CatChainBlockHash hash) const {
auto it = blocks_.find(hash);
if (it == blocks_.end()) {
return nullptr;
} else {
return it->second.get();
}
}
void CatChainImpl::destroy() {
td::actor::send_closure(receiver_, &CatChainReceiverInterface::destroy);
receiver_.release();
stop();
}
} // namespace catchain
} // namespace ton

125
catchain/catchain.h Normal file
View file

@ -0,0 +1,125 @@
/*
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 "adnl/adnl.h"
#include "adnl/utils.hpp"
#include "overlay/overlays.h"
#include "catchain-types.h"
namespace ton {
namespace catchain {
class CatChainBlock {
public:
class Extra {
public:
virtual ~Extra() = default;
};
virtual td::SharedSlice &payload() = 0;
virtual const td::SharedSlice &payload() const = 0;
virtual Extra *extra() const = 0;
virtual std::unique_ptr<Extra> move_extra() = 0;
virtual void set_extra(std::unique_ptr<Extra> extra) = 0;
virtual td::uint32 source() const = 0;
virtual td::uint32 fork() const = 0;
virtual PublicKeyHash source_hash() const = 0;
virtual CatChainBlockHash hash() const = 0;
virtual CatChainBlockHeight height() const = 0;
virtual CatChainBlock *prev() = 0;
virtual const CatChainBlock *prev() const = 0;
virtual const std::vector<CatChainBlock *> &deps() const = 0;
virtual const std::vector<CatChainBlockHeight> &vt() const = 0;
virtual bool preprocess_is_sent() const = 0;
virtual void preprocess_sent() = 0;
virtual bool is_processed() const = 0;
virtual void set_processed() = 0;
virtual bool is_descendant_of(CatChainBlock *block) = 0;
static std::unique_ptr<CatChainBlock> create(td::uint32 src, td::uint32 fork_id, PublicKeyHash src_hash,
CatChainBlockHeight height, CatChainBlockHash hash,
td::SharedSlice payload, CatChainBlock *prev,
std::vector<CatChainBlock *> deps, std::vector<CatChainBlockHeight> vt);
virtual ~CatChainBlock() = default;
};
class CatChain : public td::actor::Actor {
public:
class Callback {
public:
virtual void process_blocks(std::vector<CatChainBlock *> blocks) = 0;
virtual void finished_processing() = 0;
virtual void preprocess_block(CatChainBlock *block) = 0;
virtual void process_broadcast(PublicKeyHash src, td::BufferSlice data) = 0;
virtual void process_message(PublicKeyHash src, td::BufferSlice data) = 0;
virtual void process_query(PublicKeyHash src, td::BufferSlice data, td::Promise<td::BufferSlice> promise) = 0;
virtual void started() = 0;
virtual ~Callback() = default;
};
struct PrintId {
CatChainSessionId instance_;
PublicKeyHash local_id_;
};
virtual PrintId print_id() const = 0;
virtual void processed_block(td::BufferSlice payload) = 0;
virtual void need_new_block(td::Timestamp t) = 0;
virtual void debug_add_fork(td::BufferSlice payload, CatChainBlockHeight height) = 0;
virtual void send_broadcast(td::BufferSlice data) = 0;
virtual void send_message(PublicKeyHash dst, td::BufferSlice data) = 0;
virtual void send_query(PublicKeyHash dst, std::string name, td::Promise<td::BufferSlice> promise,
td::Timestamp timeout, td::BufferSlice query) = 0;
virtual void send_query_via(PublicKeyHash dst, std::string name, td::Promise<td::BufferSlice> promise,
td::Timestamp timeout, td::BufferSlice query, td::uint64 max_answer_size,
td::actor::ActorId<adnl::AdnlSenderInterface> via) = 0;
virtual void destroy() = 0;
static td::actor::ActorOwn<CatChain> create(std::unique_ptr<Callback> callback, CatChainOptions opts,
td::actor::ActorId<keyring::Keyring> keyring,
td::actor::ActorId<adnl::Adnl> adnl,
td::actor::ActorId<overlay::Overlays> overlay_manager,
std::vector<CatChainNode> ids, PublicKeyHash local_id,
CatChainSessionId unique_hash, std::string db_root);
virtual ~CatChain() = default;
};
} // namespace catchain
} // namespace ton
namespace td {
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChain::PrintId &print_id) {
sb << "[catchain " << print_id.instance_ << "@" << print_id.local_id_ << "]";
return sb;
}
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChain *catchain) {
sb << catchain->print_id();
return sb;
}
} // namespace td

138
catchain/catchain.hpp Normal file
View file

@ -0,0 +1,138 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <map>
#include "catchain.h"
#include "catchain-types.h"
#include "catchain-receiver-interface.h"
#include "td/utils/DecTree.h"
namespace ton {
namespace catchain {
class CatChainImpl : public CatChain {
private:
std::unique_ptr<CatChain::Callback> callback_;
CatChainOptions opts_;
td::DecTree<CatChainBlockHash, CatChainBlock *> top_blocks_;
std::map<CatChainBlockHash, std::unique_ptr<CatChainBlock>> blocks_;
std::vector<CatChainBlock *> top_source_blocks_;
std::vector<PublicKeyHash> sources_;
std::vector<bool> blamed_sources_;
std::vector<CatChainBlockHash> process_deps_;
CatChainSessionId unique_hash_;
td::uint32 local_idx_;
bool active_process_ = false;
bool force_process_ = false;
td::actor::ActorOwn<CatChainReceiverInterface> receiver_;
bool receiver_started_ = false;
std::string db_root_;
void send_process();
void send_preprocess(CatChainBlock *block);
void set_processed(CatChainBlock *block);
struct Args {
td::actor::ActorId<keyring::Keyring> keyring;
td::actor::ActorId<adnl::Adnl> adnl;
td::actor::ActorId<overlay::Overlays> overlay_manager;
std::vector<CatChainNode> ids;
PublicKeyHash local_id;
CatChainSessionId unique_hash;
Args(td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<adnl::Adnl> adnl,
td::actor::ActorId<overlay::Overlays> overlay_manager, std::vector<CatChainNode> ids, PublicKeyHash local_id,
CatChainSessionId unique_hash)
: keyring(keyring)
, adnl(adnl)
, overlay_manager(overlay_manager)
, ids(std::move(ids))
, local_id(local_id)
, unique_hash(unique_hash) {
}
};
std::unique_ptr<Args> args_;
public:
PrintId print_id() const override {
return PrintId{unique_hash_, sources_[local_idx_]};
}
CatChainBlock *get_block(CatChainBlockHash hash) const;
void on_new_block(td::uint32 src_id, td::uint32 fork, CatChainBlockHash hash, CatChainBlockHeight height,
CatChainBlockHash prev, std::vector<CatChainBlockHash> deps, std::vector<CatChainBlockHeight> vt,
td::SharedSlice data);
void on_blame(td::uint32 src_id);
void on_custom_message(PublicKeyHash src, td::BufferSlice data);
void on_custom_query(PublicKeyHash src, td::BufferSlice data, td::Promise<td::BufferSlice> promise);
void on_broadcast(PublicKeyHash src, td::BufferSlice data);
void on_receiver_started();
void processed_block(td::BufferSlice payload) override;
void need_new_block(td::Timestamp t) override;
void debug_add_fork(td::BufferSlice payload, CatChainBlockHeight height) override {
td::actor::send_closure(receiver_, &CatChainReceiverInterface::debug_add_fork, std::move(payload), height,
std::vector<CatChainBlockHash>{});
}
void send_broadcast(td::BufferSlice data) override {
td::actor::send_closure(receiver_, &CatChainReceiverInterface::send_fec_broadcast, std::move(data));
}
void send_message(PublicKeyHash dst, td::BufferSlice data) override {
td::actor::send_closure(receiver_, &CatChainReceiverInterface::send_custom_message_data, dst, std::move(data));
}
void send_query(PublicKeyHash dst, std::string name, td::Promise<td::BufferSlice> promise, td::Timestamp timeout,
td::BufferSlice query) override {
td::actor::send_closure(receiver_, &CatChainReceiverInterface::send_custom_query_data, dst, name,
std::move(promise), timeout, std::move(query));
}
void send_query_via(PublicKeyHash dst, std::string name, td::Promise<td::BufferSlice> promise, td::Timestamp timeout,
td::BufferSlice query, td::uint64 max_answer_size,
td::actor::ActorId<adnl::AdnlSenderInterface> via) override {
td::actor::send_closure(receiver_, &CatChainReceiverInterface::send_custom_query_data_via, dst, name,
std::move(promise), timeout, std::move(query), max_answer_size, via);
}
void destroy() override;
CatChainImpl(std::unique_ptr<Callback> callback, CatChainOptions opts, td::actor::ActorId<keyring::Keyring> keyring,
td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<overlay::Overlays> overlay_manager,
std::vector<CatChainNode> ids, PublicKeyHash local_id, CatChainSessionId unique_hash,
std::string db_root);
void alarm() override;
void start_up() override;
};
} // namespace catchain
} // namespace ton
namespace td {
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainImpl *catchain) {
sb << catchain->print_id();
return sb;
}
} // namespace td

21
common/CMakeLists.txt Normal file
View file

@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
set(COMMON_SOURCE
checksum.h
errorcode.h
status.h
io.hpp
errorlog.h
errorlog.cpp
)
add_library(common STATIC ${COMMON_SOURCE})
target_include_directories(common PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/..
${OPENSSL_INCLUDE_DIR}
)
target_link_libraries(common PRIVATE tdutils ton_crypto )

33
common/checksum.h Normal file
View file

@ -0,0 +1,33 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/int_types.h"
#include "crypto/common/bitstring.h"
#include "td/utils/crypto.h"
namespace td {
inline Bits256 sha256_bits256(Slice data) {
Bits256 id;
sha256(data, id.as_slice());
return id;
}
} // namespace td

52
common/delay.h Normal file
View file

@ -0,0 +1,52 @@
/*
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/actor/actor.h"
namespace ton {
template <typename T>
class DelayedAction : public td::actor::Actor {
public:
DelayedAction(T promise) : promise_(std::move(promise)) {
}
void set_timer(td::Timestamp t) {
alarm_timestamp() = t;
}
void alarm() override {
promise_();
stop();
}
static void create(T promise, td::Timestamp t) {
auto A = td::actor::create_actor<DelayedAction>("delayed", std::move(promise));
td::actor::send_closure(A, &DelayedAction::set_timer, t);
A.release();
}
private:
T promise_;
};
template <typename T>
void delay_action(T promise, td::Timestamp timeout) {
DelayedAction<T>::create(std::move(promise), timeout);
}
} // namespace ton

25
common/errorcode.h Normal file
View file

@ -0,0 +1,25 @@
/*
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
namespace ton {
enum ErrorCode : int { failure = 601, error = 602, warning = 603, protoviolation = 621, notready = 651, timeout = 652 };
}

74
common/errorlog.cpp Normal file
View file

@ -0,0 +1,74 @@
/*
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 "errorlog.h"
#include "checksum.h"
#include "td/utils/port/FileFd.h"
#include "td/utils/filesystem.h"
#include "td/utils/port/path.h"
#include "td/utils/Time.h"
#include <mutex>
namespace ton {
namespace errorlog {
td::FileFd fd;
std::mutex init_mutex_;
std::string files_path_;
void ErrorLog::create(std::string db_root) {
init_mutex_.lock();
if (!fd.empty()) {
init_mutex_.unlock();
return;
}
auto path = db_root + "/error";
td::mkdir(path).ensure();
files_path_ = path + "/files";
td::mkdir(files_path_).ensure();
auto R = td::FileFd::open(path + "/log.txt",
td::FileFd::Flags::Write | td::FileFd::Flags::Append | td::FileFd::Flags::Create);
R.ensure();
fd = R.move_as_ok();
init_mutex_.unlock();
}
void ErrorLog::log(std::string error) {
error = PSTRING() << "[" << td::Clocks::system() << "] " << error << "\n";
CHECK(!fd.empty());
auto s = td::Slice{error};
while (s.size() > 0) {
auto R = fd.write(s);
R.ensure();
s.remove_prefix(R.move_as_ok());
}
}
void ErrorLog::log_file(td::BufferSlice data) {
auto filename = sha256_bits256(data.as_slice());
auto path = files_path_ + "/" + filename.to_hex();
td::write_file(path, data.as_slice()).ensure();
}
} // namespace errorlog
} // namespace ton

37
common/errorlog.h Normal file
View file

@ -0,0 +1,37 @@
/*
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/buffer.h"
namespace ton {
namespace errorlog {
class ErrorLog {
public:
static void create(std::string db_root);
static void log(std::string error);
static void log_file(td::BufferSlice data);
};
} // namespace errorlog
} // namespace ton

45
common/io.hpp Normal file
View file

@ -0,0 +1,45 @@
/*
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/buffer.h"
#include "td/utils/misc.h"
#include "td/utils/crypto.h"
#include "td/utils/format.h"
#include "td/utils/base64.h"
#include "tl-utils/tl-utils.hpp"
#include "common/errorcode.h"
#include "common/status.h"
#include "keys/keys.hpp"
#include "crypto/common/bitstring.h"
namespace td {
template <unsigned size>
StringBuilder &operator<<(StringBuilder &stream, const td::BitArray<size> &x) {
return stream << td::base64_encode(as_slice(x));
}
inline StringBuilder &operator<<(StringBuilder &stream, const ton::PublicKeyHash &value) {
return stream << value.bits256_value();
}
} // namespace td

33
common/status.h Normal file
View file

@ -0,0 +1,33 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/Status.h"
namespace td {
inline td::Status status_prefix(td::Status &&status, std::string prefix) {
if (status.is_ok()) {
return std::move(status);
} else {
return td::Status::Error(status.code(), prefix + status.message().str());
}
}
} // namespace td

333
crypto/CMakeLists.txt Normal file
View file

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

394
crypto/Ed25519.cpp Normal file
View file

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

72
crypto/Ed25519.h Normal file
View file

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

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