Add gRPC support (#3127):

* add support for AccountInfo, Fee and Submit RPCs

* add partial support for Tx RPC (only supports Payments)
This commit is contained in:
CJ Cobb
2019-10-16 12:44:25 -07:00
parent 761bb5744e
commit 7d867b806d
112 changed files with 4428 additions and 296 deletions

3
.gitignore vendored
View File

@@ -25,6 +25,9 @@ build
.nih_c
tags
TAGS
GTAGS
GRTAGS
GPATH
bin/rippled
Debug/*.*
Release/*.*

View File

@@ -6,7 +6,7 @@ services:
env:
global:
- DOCKER_IMAGE="mellery451/rippled-ci-builder:2019-08-26"
- DOCKER_IMAGE="mellery451/rippled-ci-builder:2019-11-27_grpc"
- CMAKE_EXTRA_ARGS="-Dwerr=ON -Dwextra=ON"
- NINJA_BUILD=true
# change this if we get more VM capacity
@@ -225,6 +225,8 @@ matrix:
addons:
homebrew:
packages:
- protobuf
- grpc
- bash
- ninja
- cmake

View File

@@ -435,6 +435,7 @@ else ()
src/ripple/app/main/Application.cpp
src/ripple/app/main/BasicApp.cpp
src/ripple/app/main/CollectorManager.cpp
src/ripple/app/main/GRPCServer.cpp
src/ripple/app/main/LoadManager.cpp
src/ripple/app/main/Main.cpp
src/ripple/app/main/NodeIdentity.cpp
@@ -969,6 +970,7 @@ else ()
src/test/rpc/DepositAuthorized_test.cpp
src/test/rpc/DeliveredAmount_test.cpp
src/test/rpc/Feature_test.cpp
src/test/rpc/Fee_test.cpp
src/test/rpc/GatewayBalances_test.cpp
src/test/rpc/GetCounts_test.cpp
src/test/rpc/JSONRPC_test.cpp
@@ -987,10 +989,12 @@ else ()
src/test/rpc/RobustTransaction_test.cpp
src/test/rpc/ServerInfo_test.cpp
src/test/rpc/Status_test.cpp
src/test/rpc/Submit_test.cpp
src/test/rpc/Subscribe_test.cpp
src/test/rpc/Transaction_test.cpp
src/test/rpc/TransactionEntry_test.cpp
src/test/rpc/TransactionHistory_test.cpp
src/test/rpc/Tx_test.cpp
src/test/rpc/ValidatorRPC_test.cpp
src/test/rpc/Version_test.cpp
#[===============================[
@@ -1016,10 +1020,12 @@ target_link_libraries (rippled
Ripple::boost
Ripple::opts
Ripple::libs
Ripple::xrpl_core)
Ripple::xrpl_core
)
exclude_if_included (rippled)
# define a macro for tests that might need to
# be exluded or run differently in CI environment
if (is_ci)
target_compile_definitions(rippled PRIVATE RIPPLED_RUNNING_IN_CI)
endif ()

View File

@@ -44,7 +44,9 @@ endif ()
option (jemalloc "Enables jemalloc for heap profiling" OFF)
option (werr "treat warnings as errors" OFF)
option (local_protobuf
"Force use of a local build of protobuf instead of system version." OFF)
"Force a local build of protobuf instead of looking for an installed version." OFF)
option (local_grpc
"Force a local build of gRPC instead of looking for an installed version." OFF)
# this one is a string and therefore can't be an option
set (san "" CACHE STRING "On gcc & clang, add sanitizer instrumentation")

View File

@@ -8,8 +8,8 @@
if (static)
set (Protobuf_USE_STATIC_LIBS ON)
endif ()
find_package (Protobuf)
if (local_protobuf OR NOT TARGET protobuf::libprotobuf)
find_package (Protobuf 3.8)
if (local_protobuf OR NOT Protobuf_FOUND)
message (STATUS "using local protobuf build.")
if (WIN32)
# protobuf prepends lib even on windows
@@ -25,11 +25,12 @@ if (local_protobuf OR NOT TARGET protobuf::libprotobuf)
ExternalProject_Add (protobuf_src
PREFIX ${nih_cache_path}
GIT_REPOSITORY https://github.com/protocolbuffers/protobuf.git
GIT_TAG v3.6.1
GIT_TAG v3.8.0
SOURCE_SUBDIR cmake
CMAKE_ARGS
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_INSTALL_PREFIX=<BINARY_DIR>/_installed_
-Dprotobuf_BUILD_TESTS=OFF
-Dprotobuf_BUILD_EXAMPLES=OFF
-Dprotobuf_BUILD_PROTOC_BINARIES=ON
@@ -51,51 +52,67 @@ if (local_protobuf OR NOT TARGET protobuf::libprotobuf)
--build .
--config $<CONFIG>
$<$<VERSION_GREATER_EQUAL:${CMAKE_VERSION},3.12>:--parallel ${ep_procs}>
$<$<BOOL:${is_multiconfig}>:
COMMAND
${CMAKE_COMMAND} -E copy
<BINARY_DIR>/$<CONFIG>/${pbuf_lib_pre}protobuf$<$<CONFIG:Debug>:_d>${ep_lib_suffix}
<BINARY_DIR>
COMMAND
${CMAKE_COMMAND} -E copy
<BINARY_DIR>/$<CONFIG>/protoc${CMAKE_EXECUTABLE_SUFFIX}
<BINARY_DIR>
>
TEST_COMMAND ""
INSTALL_COMMAND ""
INSTALL_COMMAND
${CMAKE_COMMAND} -E env --unset=DESTDIR ${CMAKE_COMMAND} --build . --config $<CONFIG> --target install
BUILD_BYPRODUCTS
<BINARY_DIR>/${pbuf_lib_pre}protobuf${ep_lib_suffix}
<BINARY_DIR>/${pbuf_lib_pre}protobuf_d${ep_lib_suffix}
<BINARY_DIR>/protoc${CMAKE_EXECUTABLE_SUFFIX}
<BINARY_DIR>/_installed_/lib/${pbuf_lib_pre}protobuf${ep_lib_suffix}
<BINARY_DIR>/_installed_/lib/${pbuf_lib_pre}protobuf_d${ep_lib_suffix}
<BINARY_DIR>/_installed_/lib/${pbuf_lib_pre}protoc${ep_lib_suffix}
<BINARY_DIR>/_installed_/lib/${pbuf_lib_pre}protoc_d${ep_lib_suffix}
<BINARY_DIR>/_installed_/bin/protoc${CMAKE_EXECUTABLE_SUFFIX}
)
ExternalProject_Get_Property (protobuf_src BINARY_DIR)
ExternalProject_Get_Property (protobuf_src SOURCE_DIR)
if (CMAKE_VERBOSE_MAKEFILE)
print_ep_logs (protobuf_src)
endif ()
exclude_if_included (protobuf_src)
if (NOT TARGET protobuf::libprotobuf)
add_library (protobuf::libprotobuf STATIC IMPORTED GLOBAL)
endif ()
file (MAKE_DIRECTORY ${SOURCE_DIR}/src)
file (MAKE_DIRECTORY ${BINARY_DIR}/_installed_/include)
set_target_properties (protobuf::libprotobuf PROPERTIES
IMPORTED_LOCATION_DEBUG
${BINARY_DIR}/${pbuf_lib_pre}protobuf_d${ep_lib_suffix}
${BINARY_DIR}/_installed_/lib/${pbuf_lib_pre}protobuf_d${ep_lib_suffix}
IMPORTED_LOCATION_RELEASE
${BINARY_DIR}/${pbuf_lib_pre}protobuf${ep_lib_suffix}
${BINARY_DIR}/_installed_/lib/${pbuf_lib_pre}protobuf${ep_lib_suffix}
INTERFACE_INCLUDE_DIRECTORIES
${SOURCE_DIR}/src)
${BINARY_DIR}/_installed_/include)
add_dependencies (protobuf::libprotobuf protobuf_src)
exclude_if_included (protobuf_src)
exclude_if_included (protobuf::libprotobuf)
if (NOT TARGET protobuf::libprotoc)
add_library (protobuf::libprotoc STATIC IMPORTED GLOBAL)
endif ()
set_target_properties (protobuf::libprotoc PROPERTIES
IMPORTED_LOCATION_DEBUG
${BINARY_DIR}/_installed_/lib/${pbuf_lib_pre}protoc_d${ep_lib_suffix}
IMPORTED_LOCATION_RELEASE
${BINARY_DIR}/_installed_/lib/${pbuf_lib_pre}protoc${ep_lib_suffix}
INTERFACE_INCLUDE_DIRECTORIES
${BINARY_DIR}/_installed_/include)
add_dependencies (protobuf::libprotoc protobuf_src)
exclude_if_included (protobuf::libprotoc)
if (NOT TARGET protobuf::protoc)
add_executable (protobuf::protoc IMPORTED)
exclude_if_included (protobuf::protoc)
endif ()
set_target_properties (protobuf::protoc PROPERTIES
IMPORTED_LOCATION "${BINARY_DIR}/protoc${CMAKE_EXECUTABLE_SUFFIX}")
IMPORTED_LOCATION "${BINARY_DIR}/_installed_/bin/protoc${CMAKE_EXECUTABLE_SUFFIX}")
add_dependencies (protobuf::protoc protobuf_src)
else ()
if (NOT TARGET protobuf::protoc)
if (EXISTS "${Protobuf_PROTOC_EXECUTABLE}")
add_executable (protobuf::protoc IMPORTED)
set_target_properties (protobuf::protoc PROPERTIES
IMPORTED_LOCATION "${Protobuf_PROTOC_EXECUTABLE}")
else ()
message (FATAL_ERROR "Protobuf import failed")
endif ()
endif ()
endif ()
file (MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/proto_gen)

View File

@@ -0,0 +1,343 @@
# currently linking to unsecure versions...if we switch, we'll
# need to add ssl as a link dependency to the grpc targets
option (use_secure_grpc "use TLS version of grpc libs." OFF)
if (use_secure_grpc)
set (grpc_suffix "")
else ()
set (grpc_suffix "_unsecure")
endif ()
find_package (PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_check_modules (grpc QUIET "grpc${grpc_suffix}>=1.25" "grpc++${grpc_suffix}" gpr)
endif ()
if (grpc_FOUND)
message (STATUS "Found gRPC using pkg-config. Using ${grpc_gpr_PREFIX}.")
endif ()
add_executable (gRPC::grpc_cpp_plugin IMPORTED)
exclude_if_included (gRPC::grpc_cpp_plugin)
if (grpc_FOUND AND NOT local_grpc)
# use installed grpc (via pkg-config)
macro (add_imported_grpc libname_)
if (static)
set (_search "${CMAKE_STATIC_LIBRARY_PREFIX}${libname_}${CMAKE_STATIC_LIBRARY_SUFFIX}")
else ()
set (_search "${CMAKE_SHARED_LIBRARY_PREFIX}${libname_}${CMAKE_SHARED_LIBRARY_SUFFIX}")
endif()
find_library(_found_${libname_}
NAMES ${_search}
HINTS ${grpc_LIBRARY_DIRS})
if (_found_${libname_})
message (STATUS "importing ${libname_} as ${_found_${libname_}}")
else ()
message (FATAL_ERROR "using pkg-config for grpc, can't find ${_search}")
endif ()
add_library ("gRPC::${libname_}" STATIC IMPORTED GLOBAL)
set_target_properties ("gRPC::${libname_}" PROPERTIES IMPORTED_LOCATION ${_found_${libname_}})
if (grpc_INCLUDE_DIRS)
set_target_properties ("gRPC::${libname_}" PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${grpc_INCLUDE_DIRS})
endif ()
target_link_libraries (ripple_libs INTERFACE "gRPC::${libname_}")
exclude_if_included ("gRPC::${libname_}")
endmacro ()
set_target_properties (gRPC::grpc_cpp_plugin PROPERTIES
IMPORTED_LOCATION "${grpc_gpr_PREFIX}/bin/grpc_cpp_plugin${CMAKE_EXECUTABLE_SUFFIX}")
pkg_check_modules (cares QUIET libcares)
if (cares_FOUND)
if (static)
set (_search "${CMAKE_STATIC_LIBRARY_PREFIX}cares${CMAKE_STATIC_LIBRARY_SUFFIX}")
else ()
set (_search "${CMAKE_SHARED_LIBRARY_PREFIX}cares${CMAKE_SHARED_LIBRARY_SUFFIX}")
endif()
find_library(_cares
NAMES ${_search}
HINTS ${cares_LIBRARY_DIRS})
if (NOT _cares)
message (FATAL_ERROR "using pkg-config for grpc, can't find c-ares")
endif ()
add_library (c-ares::cares STATIC IMPORTED GLOBAL)
set_target_properties (c-ares::cares PROPERTIES IMPORTED_LOCATION ${_cares})
if (cares_INCLUDE_DIRS)
set_target_properties (c-ares::cares PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${cares_INCLUDE_DIRS})
endif ()
exclude_if_included (c-ares::cares)
else ()
message (FATAL_ERROR "using pkg-config for grpc, can't find c-ares")
endif ()
else ()
#[===========================[
c-ares (grpc requires)
#]===========================]
ExternalProject_Add (c-ares_src
PREFIX ${nih_cache_path}
GIT_REPOSITORY https://github.com/c-ares/c-ares.git
GIT_TAG cares-1_15_0
CMAKE_ARGS
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
$<$<BOOL:${CMAKE_VERBOSE_MAKEFILE}>:-DCMAKE_VERBOSE_MAKEFILE=ON>
-DCMAKE_DEBUG_POSTFIX=_d
$<$<NOT:$<BOOL:${is_multiconfig}>>:-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}>
-DCMAKE_INSTALL_PREFIX=<BINARY_DIR>/_installed_
-DCARES_SHARED=OFF
-DCARES_STATIC=ON
-DCARES_STATIC_PIC=ON
-DCARES_INSTALL=ON
-DCARES_MSVC_STATIC_RUNTIME=ON
$<$<BOOL:${MSVC}>:
"-DCMAKE_C_FLAGS=-GR -Gd -fp:precise -FS -MP"
>
LOG_BUILD ON
LOG_CONFIGURE ON
BUILD_COMMAND
${CMAKE_COMMAND}
--build .
--config $<CONFIG>
$<$<VERSION_GREATER_EQUAL:${CMAKE_VERSION},3.12>:--parallel ${ep_procs}>
TEST_COMMAND ""
INSTALL_COMMAND
${CMAKE_COMMAND} -E env --unset=DESTDIR ${CMAKE_COMMAND} --build . --config $<CONFIG> --target install
BUILD_BYPRODUCTS
<BINARY_DIR>/_installed_/lib/${ep_lib_prefix}cares${ep_lib_suffix}
<BINARY_DIR>/_installed_/lib/${ep_lib_prefix}cares_d${ep_lib_suffix}
)
exclude_if_included (c-ares_src)
ExternalProject_Get_Property (c-ares_src BINARY_DIR)
set (cares_binary_dir "${BINARY_DIR}")
add_library (c-ares::cares STATIC IMPORTED GLOBAL)
file (MAKE_DIRECTORY ${BINARY_DIR}/_installed_/include)
set_target_properties (c-ares::cares PROPERTIES
IMPORTED_LOCATION_DEBUG
${BINARY_DIR}/_installed_/lib/${ep_lib_prefix}cares_d${ep_lib_suffix}
IMPORTED_LOCATION_RELEASE
${BINARY_DIR}/_installed_/lib/${ep_lib_prefix}cares${ep_lib_suffix}
INTERFACE_INCLUDE_DIRECTORIES
${BINARY_DIR}/_installed_/include)
add_dependencies (c-ares::cares c-ares_src)
exclude_if_included (c-ares::cares)
if (NOT has_zlib)
#[===========================[
zlib (grpc requires)
#]===========================]
if (MSVC)
set (zlib_debug_postfix "d") # zlib cmake sets this internally for MSVC, so we really don't have a choice
set (zlib_base "zlibstatic")
else ()
set (zlib_debug_postfix "_d")
set (zlib_base "z")
endif ()
ExternalProject_Add (zlib_src
PREFIX ${nih_cache_path}
GIT_REPOSITORY https://github.com/madler/zlib.git
GIT_TAG v1.2.11
CMAKE_ARGS
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
$<$<BOOL:${CMAKE_VERBOSE_MAKEFILE}>:-DCMAKE_VERBOSE_MAKEFILE=ON>
-DCMAKE_DEBUG_POSTFIX=${zlib_debug_postfix}
$<$<NOT:$<BOOL:${is_multiconfig}>>:-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}>
-DCMAKE_INSTALL_PREFIX=<BINARY_DIR>/_installed_
-DBUILD_SHARED_LIBS=OFF
$<$<BOOL:${MSVC}>:
"-DCMAKE_C_FLAGS=-GR -Gd -fp:precise -FS -MP"
"-DCMAKE_C_FLAGS_DEBUG=-MTd"
"-DCMAKE_C_FLAGS_RELEASE=-MT"
>
LOG_BUILD ON
LOG_CONFIGURE ON
BUILD_COMMAND
${CMAKE_COMMAND}
--build .
--config $<CONFIG>
$<$<VERSION_GREATER_EQUAL:${CMAKE_VERSION},3.12>:--parallel ${ep_procs}>
TEST_COMMAND ""
INSTALL_COMMAND
${CMAKE_COMMAND} -E env --unset=DESTDIR ${CMAKE_COMMAND} --build . --config $<CONFIG> --target install
BUILD_BYPRODUCTS
<BINARY_DIR>/_installed_/lib/${ep_lib_prefix}${zlib_base}${ep_lib_suffix}
<BINARY_DIR>/_installed_/lib/${ep_lib_prefix}${zlib_base}${zlib_debug_postfix}${ep_lib_suffix}
)
exclude_if_included (zlib_src)
ExternalProject_Get_Property (zlib_src BINARY_DIR)
set (zlib_binary_dir "${BINARY_DIR}")
add_library (ZLIB::ZLIB STATIC IMPORTED GLOBAL)
file (MAKE_DIRECTORY ${BINARY_DIR}/_installed_/include)
set_target_properties (ZLIB::ZLIB PROPERTIES
IMPORTED_LOCATION_DEBUG
${BINARY_DIR}/_installed_/lib/${ep_lib_prefix}${zlib_base}${zlib_debug_postfix}${ep_lib_suffix}
IMPORTED_LOCATION_RELEASE
${BINARY_DIR}/_installed_/lib/${ep_lib_prefix}${zlib_base}${ep_lib_suffix}
INTERFACE_INCLUDE_DIRECTORIES
${BINARY_DIR}/_installed_/include)
add_dependencies (ZLIB::ZLIB zlib_src)
exclude_if_included (ZLIB::ZLIB)
endif ()
#[===========================[
grpc
#]===========================]
ExternalProject_Add (grpc_src
PREFIX ${nih_cache_path}
GIT_REPOSITORY https://github.com/grpc/grpc.git
GIT_TAG v1.25.0
CMAKE_ARGS
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
$<$<BOOL:${CMAKE_VERBOSE_MAKEFILE}>:-DCMAKE_VERBOSE_MAKEFILE=ON>
$<$<BOOL:${CMAKE_TOOLCHAIN_FILE}>:-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}>
$<$<BOOL:${VCPKG_TARGET_TRIPLET}>:-DVCPKG_TARGET_TRIPLET=${VCPKG_TARGET_TRIPLET}>
-DCMAKE_DEBUG_POSTFIX=_d
$<$<NOT:$<BOOL:${is_multiconfig}>>:-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}>
-DgRPC_BUILD_TESTS=OFF
-DgRPC_BUILD_CSHARP_EXT=OFF
-DgRPC_MSVC_STATIC_RUNTIME=ON
-DgRPC_INSTALL=OFF
-DgRPC_CARES_PROVIDER=package
-Dc-ares_DIR=${cares_binary_dir}/_installed_/lib/cmake/c-ares
-DgRPC_SSL_PROVIDER=package
-DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR}
-DgRPC_PROTOBUF_PROVIDER=package
-DProtobuf_USE_STATIC_LIBS=$<IF:$<AND:$<BOOL:${Protobuf_FOUND}>,$<NOT:$<BOOL:${static}>>>,OFF,ON>
-DProtobuf_INCLUDE_DIR=$<JOIN:$<TARGET_PROPERTY:protobuf::libprotobuf,INTERFACE_INCLUDE_DIRECTORIES>,:_:>
-DProtobuf_LIBRARY=$<IF:$<CONFIG:Debug>,$<TARGET_PROPERTY:protobuf::libprotobuf,IMPORTED_LOCATION_DEBUG>,$<TARGET_PROPERTY:protobuf::libprotobuf,IMPORTED_LOCATION_RELEASE>>
-DProtobuf_PROTOC_LIBRARY=$<IF:$<CONFIG:Debug>,$<TARGET_PROPERTY:protobuf::libprotoc,IMPORTED_LOCATION_DEBUG>,$<TARGET_PROPERTY:protobuf::libprotoc,IMPORTED_LOCATION_RELEASE>>
-DProtobuf_PROTOC_EXECUTABLE=$<TARGET_PROPERTY:protobuf::protoc,IMPORTED_LOCATION>
-DgRPC_ZLIB_PROVIDER=package
$<$<NOT:$<BOOL:${has_zlib}>>:-DZLIB_ROOT=${zlib_binary_dir}/_installed_>
$<$<BOOL:${MSVC}>:
"-DCMAKE_CXX_FLAGS=-GR -Gd -fp:precise -FS -EHa -MP"
"-DCMAKE_C_FLAGS=-GR -Gd -fp:precise -FS -MP"
>
LOG_BUILD ON
LOG_CONFIGURE ON
BUILD_COMMAND
${CMAKE_COMMAND}
--build .
--config $<CONFIG>
$<$<VERSION_GREATER_EQUAL:${CMAKE_VERSION},3.12>:--parallel ${ep_procs}>
$<$<BOOL:${is_multiconfig}>:
COMMAND
${CMAKE_COMMAND} -E copy
<BINARY_DIR>/$<CONFIG>/${ep_lib_prefix}grpc${grpc_suffix}$<$<CONFIG:Debug>:_d>${ep_lib_suffix}
<BINARY_DIR>/$<CONFIG>/${ep_lib_prefix}grpc++${grpc_suffix}$<$<CONFIG:Debug>:_d>${ep_lib_suffix}
<BINARY_DIR>/$<CONFIG>/${ep_lib_prefix}address_sorting$<$<CONFIG:Debug>:_d>${ep_lib_suffix}
<BINARY_DIR>/$<CONFIG>/${ep_lib_prefix}gpr$<$<CONFIG:Debug>:_d>${ep_lib_suffix}
<BINARY_DIR>/$<CONFIG>/grpc_cpp_plugin${CMAKE_EXECUTABLE_SUFFIX}
<BINARY_DIR>
>
LIST_SEPARATOR :_:
TEST_COMMAND ""
INSTALL_COMMAND ""
DEPENDS c-ares_src
BUILD_BYPRODUCTS
<BINARY_DIR>/${ep_lib_prefix}grpc${grpc_suffix}${ep_lib_suffix}
<BINARY_DIR>/${ep_lib_prefix}grpc${grpc_suffix}_d${ep_lib_suffix}
<BINARY_DIR>/${ep_lib_prefix}grpc++${grpc_suffix}${ep_lib_suffix}
<BINARY_DIR>/${ep_lib_prefix}grpc++${grpc_suffix}_d${ep_lib_suffix}
<BINARY_DIR>/${ep_lib_prefix}address_sorting${ep_lib_suffix}
<BINARY_DIR>/${ep_lib_prefix}address_sorting_d${ep_lib_suffix}
<BINARY_DIR>/${ep_lib_prefix}gpr${ep_lib_suffix}
<BINARY_DIR>/${ep_lib_prefix}gpr_d${ep_lib_suffix}
<BINARY_DIR>/grpc_cpp_plugin${CMAKE_EXECUTABLE_SUFFIX}
)
if (TARGET protobuf_src)
ExternalProject_Add_StepDependencies(grpc_src build protobuf_src)
endif ()
exclude_if_included (grpc_src)
ExternalProject_Get_Property (grpc_src BINARY_DIR)
ExternalProject_Get_Property (grpc_src SOURCE_DIR)
set (grpc_binary_dir "${BINARY_DIR}")
set (grpc_source_dir "${SOURCE_DIR}")
if (CMAKE_VERBOSE_MAKEFILE)
print_ep_logs (grpc_src)
endif ()
file (MAKE_DIRECTORY ${SOURCE_DIR}/include)
macro (add_imported_grpc libname_)
add_library ("gRPC::${libname_}" STATIC IMPORTED GLOBAL)
set_target_properties ("gRPC::${libname_}" PROPERTIES
IMPORTED_LOCATION_DEBUG
${grpc_binary_dir}/${ep_lib_prefix}${libname_}_d${ep_lib_suffix}
IMPORTED_LOCATION_RELEASE
${grpc_binary_dir}/${ep_lib_prefix}${libname_}${ep_lib_suffix}
INTERFACE_INCLUDE_DIRECTORIES
${grpc_source_dir}/include)
add_dependencies ("gRPC::${libname_}" grpc_src)
target_link_libraries (ripple_libs INTERFACE "gRPC::${libname_}")
exclude_if_included ("gRPC::${libname_}")
endmacro ()
set_target_properties (gRPC::grpc_cpp_plugin PROPERTIES
IMPORTED_LOCATION "${grpc_binary_dir}/grpc_cpp_plugin${CMAKE_EXECUTABLE_SUFFIX}")
add_dependencies (gRPC::grpc_cpp_plugin grpc_src)
endif ()
add_imported_grpc (gpr)
add_imported_grpc ("grpc${grpc_suffix}")
add_imported_grpc ("grpc++${grpc_suffix}")
add_imported_grpc (address_sorting)
target_link_libraries ("gRPC::grpc${grpc_suffix}" INTERFACE c-ares::cares gRPC::gpr gRPC::address_sorting ZLIB::ZLIB)
target_link_libraries ("gRPC::grpc++${grpc_suffix}" INTERFACE "gRPC::grpc${grpc_suffix}" gRPC::gpr)
#[=================================[
generate protobuf sources for
grpc defs and bundle into a
static lib
#]=================================]
set (GRPC_GEN_DIR "${CMAKE_BINARY_DIR}/proto_gen_grpc")
file (MAKE_DIRECTORY ${GRPC_GEN_DIR})
set (GRPC_PROTO_SRCS)
set (GRPC_PROTO_HDRS)
set (GRPC_PROTO_ROOT "${CMAKE_SOURCE_DIR}/src/ripple/proto/rpc")
file(GLOB_RECURSE GRPC_DEFINITION_FILES LIST_DIRECTORIES false "${GRPC_PROTO_ROOT}/*.proto")
foreach(file ${GRPC_DEFINITION_FILES})
get_filename_component(_abs_file ${file} ABSOLUTE)
get_filename_component(_abs_dir ${_abs_file} DIRECTORY)
get_filename_component(_basename ${file} NAME_WE)
get_filename_component(_proto_inc ${GRPC_PROTO_ROOT} DIRECTORY) # updir one level
file(RELATIVE_PATH _rel_root_file ${_proto_inc} ${_abs_file})
get_filename_component(_rel_root_dir ${_rel_root_file} DIRECTORY)
file(RELATIVE_PATH _rel_dir ${CMAKE_CURRENT_SOURCE_DIR} ${_abs_dir})
set (src_1 "${GRPC_GEN_DIR}/${_rel_root_dir}/${_basename}.grpc.pb.cc")
set (src_2 "${GRPC_GEN_DIR}/${_rel_root_dir}/${_basename}.pb.cc")
set (hdr_1 "${GRPC_GEN_DIR}/${_rel_root_dir}/${_basename}.grpc.pb.h")
set (hdr_2 "${GRPC_GEN_DIR}/${_rel_root_dir}/${_basename}.pb.h")
add_custom_command(
OUTPUT ${src_1} ${src_2} ${hdr_1} ${hdr_2}
COMMAND protobuf::protoc
ARGS --grpc_out=${GRPC_GEN_DIR}
--cpp_out=${GRPC_GEN_DIR}
--plugin=protoc-gen-grpc=$<TARGET_FILE:gRPC::grpc_cpp_plugin>
-I ${_proto_inc} -I ${_rel_dir}
${_abs_file}
DEPENDS ${_abs_file} protobuf::protoc gRPC::grpc_cpp_plugin
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Running gRPC C++ protocol buffer compiler on ${file}"
VERBATIM)
set_source_files_properties(${src_1} ${src_2} ${hdr_1} ${hdr_2} PROPERTIES GENERATED TRUE)
list(APPEND GRPC_PROTO_SRCS ${src_1} ${src_2})
list(APPEND GRPC_PROTO_HDRS ${hdr_1} ${hdr_2})
endforeach()
add_library (grpc_pbufs STATIC ${GRPC_PROTO_SRCS} ${GRPC_PROTO_HDRS})
#target_include_directories (grpc_pbufs PRIVATE src)
target_include_directories (grpc_pbufs SYSTEM PUBLIC ${GRPC_GEN_DIR})
target_link_libraries (grpc_pbufs protobuf::libprotobuf "gRPC::grpc++${grpc_suffix}")
target_compile_options (grpc_pbufs
PUBLIC
$<$<BOOL:${is_xcode}>:
--system-header-prefix="google/protobuf"
-Wno-deprecated-dynamic-exception-spec
>)
add_library (Ripple::grpc_pbufs ALIAS grpc_pbufs)
target_link_libraries (ripple_libs INTERFACE Ripple::grpc_pbufs)
exclude_if_included (grpc_pbufs)

View File

@@ -7,7 +7,7 @@ yum -y upgrade
yum -y update
yum -y install epel-release centos-release-scl
yum -y install \
wget curl time gcc-c++ time yum-utils \
wget curl time gcc-c++ time yum-utils autoconf pkgconfig \
libstdc++-static rpm-build gnupg which make cmake \
devtoolset-7 devtoolset-7-gdb devtoolset-7-libasan-devel devtoolset-7-libtsan-devel devtoolset-7-libubsan-devel \
devtoolset-8 devtoolset-8-gdb devtoolset-8-binutils devtoolset-8-libstdc++-devel \
@@ -22,8 +22,6 @@ yum -y install \
python-devel python27-python-devel rh-python35-python-devel \
python27 rh-python35 \
ninja-build git svn \
protobuf protobuf-static protobuf-c-devel \
protobuf-compiler protobuf-devel \
swig perl-Digest-MD5 python2-pip
if [ "${CI_USE}" = true ] ; then

View File

@@ -2,7 +2,7 @@ Source: rippled
Section: misc
Priority: extra
Maintainer: Ripple Labs Inc. <support@ripple.com>
Build-Depends: cmake, debhelper (>=9), libprotobuf-dev, libssl-dev, zlib1g-dev, dh-systemd, ninja-build
Build-Depends: cmake, debhelper (>=9), zlib1g-dev, dh-systemd, ninja-build
Standards-Version: 3.9.7
Homepage: http://ripple.com/

View File

@@ -20,7 +20,6 @@ override_dh_auto_configure:
-DCMAKE_INSTALL_PREFIX=/opt/ripple \
-DCMAKE_BUILD_TYPE=Release \
-Dstatic=true \
-Dlocal_protobuf=ON \
-Dvalidator_keys=ON \
-DCMAKE_VERBOSE_MAKEFILE=ON

View File

@@ -12,7 +12,7 @@ License: MIT
URL: http://ripple.com/
Source0: rippled.tar.gz
BuildRequires: protobuf-static openssl-static cmake zlib-static ninja-build
BuildRequires: cmake zlib-static ninja-build
%description
rippled
@@ -32,7 +32,7 @@ core library for development of standalone applications that sign transactions.
cd rippled
mkdir -p bld.release
cd bld.release
cmake .. -G Ninja -DCMAKE_INSTALL_PREFIX=%{_prefix} -DCMAKE_BUILD_TYPE=Release -Dstatic=true -DCMAKE_VERBOSE_MAKEFILE=ON -Dlocal_protobuf=ON -Dvalidator_keys=ON
cmake .. -G Ninja -DCMAKE_INSTALL_PREFIX=%{_prefix} -DCMAKE_BUILD_TYPE=Release -Dstatic=true -DCMAKE_VERBOSE_MAKEFILE=ON -Dvalidator_keys=ON
cmake --build . --parallel --target rippled --target validator-keys -- -v
%pre

View File

@@ -36,6 +36,40 @@ rm -f openssl-${OPENSSL_VER}.tar.gz
rm -rf openssl-${OPENSSL_VER}
LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-}:/opt/local/openssl/lib /opt/local/openssl/bin/openssl version -a
cd /tmp
wget https://github.com/protocolbuffers/protobuf/releases/download/v3.10.1/protobuf-all-3.10.1.tar.gz
tar xf protobuf-all-3.10.1.tar.gz
cd protobuf-3.10.1
./autogen.sh
./configure
make -j$(nproc)
make install
ldconfig
cd ..
rm -f protobuf-all-3.10.1.tar.gz
rm -rf protobuf-3.10.1
cd /tmp
wget https://github.com/c-ares/c-ares/releases/download/cares-1_15_0/c-ares-1.15.0.tar.gz
tar xf c-ares-1.15.0.tar.gz
cd c-ares-1.15.0
./configure
make -j$(nproc)
make install
cd ..
rm -f c-ares-1.15.0.tar.gz
rm -rf c-ares-1.15.0
cd /tmp
wget https://github.com/grpc/grpc/archive/v1.25.0.tar.gz
tar xf v1.25.0.tar.gz
cd grpc-1.25.0
make -j$(nproc)
make install
cd ..
rm -f xf v1.25.0.tar.gz
rm -rf grpc-1.25.0
if [ "${CI_USE}" = true ] ; then
cd /tmp
wget https://github.com/doxygen/doxygen/archive/Release_1_8_16.tar.gz

View File

@@ -30,8 +30,8 @@ apt-get -y clean
apt-get -y update
apt-get -y --fix-missing install \
make cmake ninja-build \
protobuf-compiler libprotobuf-dev openssl libssl-dev \
make cmake ninja-build autoconf libtool pkg-config \
openssl libssl-dev \
liblzma-dev libbz2-dev zlib1g-dev \
libjemalloc-dev \
python-pip \

View File

@@ -54,6 +54,7 @@ include(deps/Rocksdb)
include(deps/Nudb)
include(deps/date)
include(deps/Protobuf)
include(deps/gRPC)
###

View File

@@ -17,7 +17,7 @@ if [[ -d "${VCPKG_DIR}" ]] ; then
rm -rf "${VCPKG_DIR}"
fi
git clone --branch 2019.06 https://github.com/Microsoft/vcpkg.git ${VCPKG_DIR}
git clone --branch 2019.10 https://github.com/Microsoft/vcpkg.git ${VCPKG_DIR}
pushd ${VCPKG_DIR}
if [[ -z ${COMSPEC:-} ]]; then
chmod +x ./bootstrap-vcpkg.sh

View File

@@ -1168,6 +1168,10 @@ ip = 127.0.0.1
admin = 127.0.0.1
protocol = ws
#[port_grpc]
#port = 50051
#ip = 0.0.0.0
#[port_ws_public]
#port = 6005
#ip = 127.0.0.1

View File

@@ -66,6 +66,7 @@
#include <ripple/beast/core/LexicalCast.h>
#include <boost/algorithm/string/predicate.hpp>
#include <ripple/app/main/GRPCServer.h>
#include <boost/asio/steady_timer.hpp>
#include <boost/system/error_code.hpp>
@@ -391,6 +392,8 @@ public:
io_latency_sampler m_io_latency_sampler;
std::unique_ptr<GRPCServer> grpcServer_;
//--------------------------------------------------------------------------
static
@@ -552,6 +555,7 @@ public:
, m_io_latency_sampler (m_collectorManager->collector()->make_event ("ios_latency"),
logs_->journal("Application"), std::chrono::milliseconds (100), get_io_service())
, grpcServer_(std::make_unique<GRPCServer>(*this))
{
if (shardStore_)
{
@@ -1339,6 +1343,7 @@ bool ApplicationImp::setup()
logs_->silent (config_->silent());
m_jobQueue->setThreadCount (config_->WORKERS, config_->standalone());
grpcServer_->run();
if (!config_->standalone())
timeKeeper_->run(config_->SNTP_SERVERS);
@@ -1583,9 +1588,15 @@ bool ApplicationImp::setup()
Resource::Charge loadType = Resource::feeReferenceRPC;
Resource::Consumer c;
RPC::Context context { journal ("RPCHandler"), jvCommand, *this,
loadType, getOPs (), getLedgerMaster(), c, Role::ADMIN,
RPC::ApiMaximumSupportedVersion};
RPC::JsonContext context{{journal("RPCHandler"),
*this,
loadType,
getOPs(),
getLedgerMaster(),
c,
Role::ADMIN},
jvCommand,
RPC::ApiMaximumSupportedVersion};
Json::Value jvResult;
RPC::doCommand (context, jvResult);

View File

@@ -0,0 +1,408 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/app/main/GRPCServer.h>
#include <ripple/resource/Fees.h>
namespace ripple {
namespace {
// helper function. strips scheme from endpoint string
std::string
getEndpoint(std::string const& peer)
{
std::size_t first = peer.find_first_of(":");
std::size_t last = peer.find_last_of(":");
std::string peerClean(peer);
if (first != last)
{
peerClean = peer.substr(first + 1);
}
return peerClean;
}
} // namespace
template <class Request, class Response>
GRPCServerImpl::CallData<Request, Response>::CallData(
rpc::v1::XRPLedgerAPIService::AsyncService& service,
grpc::ServerCompletionQueue& cq,
Application& app,
BindListener<Request, Response> bindListener,
Handler<Request, Response> handler,
RPC::Condition requiredCondition,
Resource::Charge loadType)
: service_(service)
, cq_(cq)
, finished_(false)
, app_(app)
, aborted_(false)
, responder_(&ctx_)
, bindListener_(std::move(bindListener))
, handler_(std::move(handler))
, requiredCondition_(std::move(requiredCondition))
, loadType_(std::move(loadType))
{
// Bind a listener. When a request is received, "this" will be returned
// from CompletionQueue::Next
bindListener_(service_, &ctx_, &request_, &responder_, &cq_, &cq_, this);
}
template <class Request, class Response>
std::shared_ptr<Processor>
GRPCServerImpl::CallData<Request, Response>::clone()
{
return std::make_shared<CallData<Request, Response>>(
service_,
cq_,
app_,
bindListener_,
handler_,
requiredCondition_,
loadType_);
}
template <class Request, class Response>
void
GRPCServerImpl::CallData<Request, Response>::process()
{
// sanity check
BOOST_ASSERT(!finished_);
std::shared_ptr<CallData<Request, Response>> thisShared =
this->shared_from_this();
app_.getJobQueue().postCoro(
JobType::jtRPC,
"gRPC-Client",
[thisShared](std::shared_ptr<JobQueue::Coro> coro) {
std::lock_guard lock{thisShared->mut_};
// Do nothing if call has been aborted due to server shutdown
// or if handler was already executed
if (thisShared->aborted_ || thisShared->finished_)
return;
thisShared->process(coro);
thisShared->finished_ = true;
});
}
template <class Request, class Response>
void
GRPCServerImpl::CallData<Request, Response>::process(
std::shared_ptr<JobQueue::Coro> coro)
{
try
{
auto usage = getUsage();
if (usage.disconnect())
{
grpc::Status status{grpc::StatusCode::RESOURCE_EXHAUSTED,
"usage balance exceeds threshhold"};
responder_.FinishWithError(status, this);
}
else
{
auto loadType = getLoadType();
usage.charge(loadType);
auto role = getRole();
RPC::GRPCContext<Request> context{{app_.journal("gRPCServer"),
app_,
loadType,
app_.getOPs(),
app_.getLedgerMaster(),
usage,
role,
coro,
InfoSub::pointer()},
request_};
// Make sure we can currently handle the rpc
error_code_i conditionMetRes =
RPC::conditionMet(requiredCondition_, context);
if (conditionMetRes != rpcSUCCESS)
{
RPC::ErrorInfo errorInfo = RPC::get_error_info(conditionMetRes);
grpc::Status status{grpc::StatusCode::INTERNAL,
errorInfo.message.c_str()};
responder_.FinishWithError(status, this);
}
else
{
std::pair<Response, grpc::Status> result = handler_(context);
responder_.Finish(result.first, result.second, this);
}
}
}
catch (std::exception const& ex)
{
grpc::Status status{grpc::StatusCode::INTERNAL, ex.what()};
responder_.FinishWithError(status, this);
}
}
template <class Request, class Response>
bool
GRPCServerImpl::CallData<Request, Response>::isFinished()
{
// Need to lock here because this object can be returned from cq_.Next(..)
// as soon as the response is sent, which could be before finished_ is set
// to true, causing the handler to be executed twice
std::lock_guard lock{mut_};
return finished_;
}
template <class Request, class Response>
void
GRPCServerImpl::CallData<Request, Response>::abort()
{
std::lock_guard lock{mut_};
aborted_ = true;
}
template <class Request, class Response>
Resource::Charge
GRPCServerImpl::CallData<Request, Response>::getLoadType()
{
return loadType_;
}
template <class Request, class Response>
Role
GRPCServerImpl::CallData<Request, Response>::getRole()
{
return Role::USER;
}
template <class Request, class Response>
Resource::Consumer
GRPCServerImpl::CallData<Request, Response>::getUsage()
{
std::string peer = getEndpoint(ctx_.peer());
boost::optional<beast::IP::Endpoint> endpoint =
beast::IP::Endpoint::from_string_checked(peer);
return app_.getResourceManager().newInboundEndpoint(endpoint.get());
}
GRPCServerImpl::GRPCServerImpl(Application& app) : app_(app)
{
// if present, get endpoint from config
if (app_.config().exists("port_grpc"))
{
Section section = app_.config().section("port_grpc");
std::pair<std::string, bool> ipPair = section.find("ip");
if (!ipPair.second)
return;
std::pair<std::string, bool> portPair = section.find("port");
if (!portPair.second)
return;
try
{
beast::IP::Endpoint endpoint(
boost::asio::ip::make_address(ipPair.first),
std::stoi(portPair.first));
serverAddress_ = endpoint.to_string();
}
catch (std::exception const&)
{
}
}
}
void
GRPCServerImpl::shutdown()
{
server_->Shutdown();
// Always shutdown the completion queue after the server.
cq_->Shutdown();
}
void
GRPCServerImpl::handleRpcs()
{
// This collection should really be an unordered_set. However, to delete
// from the unordered_set, we need a shared_ptr, but cq_.Next() (see below
// while loop) sets the tag to a raw pointer.
std::vector<std::shared_ptr<Processor>> requests = setupListeners();
auto erase = [&requests](Processor* ptr) {
auto it = std::find_if(
requests.begin(),
requests.end(),
[ptr](std::shared_ptr<Processor>& sPtr) {
return sPtr.get() == ptr;
});
BOOST_ASSERT(it != requests.end());
it->swap(requests.back());
requests.pop_back();
};
void* tag; // uniquely identifies a request.
bool ok;
// Block waiting to read the next event from the completion queue. The
// event is uniquely identified by its tag, which in this case is the
// memory address of a CallData instance.
// The return value of Next should always be checked. This return value
// tells us whether there is any kind of event or cq_ is shutting down.
while (cq_->Next(&tag, &ok))
{
auto ptr = static_cast<Processor*>(tag);
// if ok is false, event was terminated as part of a shutdown sequence
// need to abort any further processing
if (!ok)
{
// abort first, then erase. Otherwise, erase can delete object
ptr->abort();
erase(ptr);
}
else
{
if (!ptr->isFinished())
{
// ptr is now processing a request, so create a new CallData
// object to handle additional requests
auto cloned = ptr->clone();
requests.push_back(cloned);
// process the request
ptr->process();
}
else
{
erase(ptr);
}
}
}
}
// create a CallData instance for each RPC
std::vector<std::shared_ptr<Processor>>
GRPCServerImpl::setupListeners()
{
std::vector<std::shared_ptr<Processor>> requests;
auto addToRequests = [&requests](auto callData) {
requests.push_back(std::move(callData));
};
{
using cd = CallData<rpc::v1::GetFeeRequest, rpc::v1::GetFeeResponse>;
addToRequests(std::make_shared<cd>(
service_,
*cq_,
app_,
&rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetFee,
doFeeGrpc,
RPC::NEEDS_CURRENT_LEDGER,
Resource::feeReferenceRPC));
}
{
using cd = CallData<
rpc::v1::GetAccountInfoRequest,
rpc::v1::GetAccountInfoResponse>;
addToRequests(std::make_shared<cd>(
service_,
*cq_,
app_,
&rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetAccountInfo,
doAccountInfoGrpc,
RPC::NEEDS_CURRENT_LEDGER,
Resource::feeReferenceRPC));
}
{
using cd = CallData<rpc::v1::GetTxRequest, rpc::v1::GetTxResponse>;
addToRequests(std::make_shared<cd>(
service_,
*cq_,
app_,
&rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetTx,
doTxGrpc,
RPC::NEEDS_CURRENT_LEDGER,
Resource::feeReferenceRPC));
}
{
using cd = CallData<
rpc::v1::SubmitTransactionRequest,
rpc::v1::SubmitTransactionResponse>;
addToRequests(std::make_shared<cd>(
service_,
*cq_,
app_,
&rpc::v1::XRPLedgerAPIService::AsyncService::
RequestSubmitTransaction,
doSubmitGrpc,
RPC::NEEDS_CURRENT_LEDGER,
Resource::feeMediumBurdenRPC));
}
return requests;
};
bool
GRPCServerImpl::start()
{
// if config does not specify a grpc server address, don't start
if (serverAddress_.empty())
return false;
grpc::ServerBuilder builder;
// Listen on the given address without any authentication mechanism.
builder.AddListeningPort(serverAddress_, grpc::InsecureServerCredentials());
// Register "service_" as the instance through which we'll communicate with
// clients. In this case it corresponds to an *asynchronous* service.
builder.RegisterService(&service_);
// Get hold of the completion queue used for the asynchronous communication
// with the gRPC runtime.
cq_ = builder.AddCompletionQueue();
// Finally assemble the server.
server_ = builder.BuildAndStart();
return true;
}
void
GRPCServer::run()
{
// Start the server and setup listeners
if ((running_ = impl_.start()))
{
thread_ = std::thread([this]() {
// Start the event loop and begin handling requests
this->impl_.handleRpcs();
});
}
}
GRPCServer::~GRPCServer()
{
if (running_)
{
impl_.shutdown();
thread_.join();
}
}
} // namespace ripple

View File

@@ -0,0 +1,263 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CORE_GRPCSERVER_H_INCLUDED
#define RIPPLE_CORE_GRPCSERVER_H_INCLUDED
#include <ripple/app/main/Application.h>
#include <ripple/core/JobQueue.h>
#include <ripple/net/InfoSub.h>
#include <ripple/protocol/ErrorCodes.h>
#include <ripple/resource/Charge.h>
#include <ripple/rpc/Context.h>
#include <ripple/rpc/GRPCHandlers.h>
#include <ripple/rpc/Role.h>
#include <ripple/rpc/impl/Handler.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <ripple/rpc/impl/Tuning.h>
#include "rpc/v1/xrp_ledger.grpc.pb.h"
#include <grpcpp/grpcpp.h>
namespace ripple {
// Interface that CallData implements
class Processor
{
public:
virtual ~Processor() = default;
Processor() = default;
Processor(const Processor&) = delete;
Processor&
operator=(const Processor&) = delete;
// process a request that has arrived. Can only be called once per instance
virtual void
process() = 0;
// abort processing this request. called when server shutsdown
virtual void
abort() = 0;
// create a new instance of this CallData object, with the same type
//(same template parameters) as original. This is called when a CallData
// object starts processing a request. Creating a new instance allows the
// server to handle additional requests while the first is being processed
virtual std::shared_ptr<Processor>
clone() = 0;
// true if this object has finished processing the request. Object will be
// deleted once this function returns true
virtual bool
isFinished() = 0;
};
class GRPCServerImpl final
{
private:
// CompletionQueue returns events that have occurred, or events that have
// been cancelled
std::unique_ptr<grpc::ServerCompletionQueue> cq_;
std::vector<std::shared_ptr<Processor>> requests_;
// The gRPC service defined by the .proto files
rpc::v1::XRPLedgerAPIService::AsyncService service_;
std::unique_ptr<grpc::Server> server_;
Application& app_;
std::string serverAddress_;
// typedef for function to bind a listener
// This is always of the form:
// rpc::v1::XRPLedgerAPIService::AsyncService::Request[RPC NAME]
template <class Request, class Response>
using BindListener = std::function<void(
rpc::v1::XRPLedgerAPIService::AsyncService&,
grpc::ServerContext*,
Request*,
grpc::ServerAsyncResponseWriter<Response>*,
grpc::CompletionQueue*,
grpc::ServerCompletionQueue*,
void*)>;
// typedef for actual handler (that populates a response)
// handlers are defined in rpc/GRPCHandlers.h
template <class Request, class Response>
using Handler = std::function<std::pair<Response, grpc::Status>(
RPC::GRPCContext<Request>&)>;
public:
explicit GRPCServerImpl(Application& app);
GRPCServerImpl(const GRPCServerImpl&) = delete;
GRPCServerImpl&
operator=(const GRPCServerImpl&) = delete;
void
shutdown();
// setup the server and listeners
// returns true if server started successfully
bool
start();
// the main event loop
void
handleRpcs();
// Create a CallData object for each RPC. Return created objects in vector
std::vector<std::shared_ptr<Processor>>
setupListeners();
private:
// Class encompasing the state and logic needed to serve a request.
template <class Request, class Response>
class CallData
: public Processor,
public std::enable_shared_from_this<CallData<Request, Response>>
{
private:
// The means of communication with the gRPC runtime for an asynchronous
// server.
rpc::v1::XRPLedgerAPIService::AsyncService& service_;
// The producer-consumer queue for asynchronous server notifications.
grpc::ServerCompletionQueue& cq_;
// Context for the rpc, allowing to tweak aspects of it such as the use
// of compression, authentication, as well as to send metadata back to
// the client.
grpc::ServerContext ctx_;
// true if finished processing request
bool finished_;
Application& app_;
// mutex for signaling abort
std::mutex mut_;
// whether the call should be aborted, due to server shutdown
bool aborted_;
// What we get from the client.
Request request_;
// What we send back to the client.
Response reply_;
// The means to get back to the client.
grpc::ServerAsyncResponseWriter<Response> responder_;
// Function that creates a listener for specific request type
BindListener<Request, Response> bindListener_;
// Function that processes a request
Handler<Request, Response> handler_;
// Condition required for this RPC
RPC::Condition requiredCondition_;
// Load type for this RPC
Resource::Charge loadType_;
public:
virtual ~CallData() = default;
// Take in the "service" instance (in this case representing an
// asynchronous server) and the completion queue "cq" used for
// asynchronous communication with the gRPC runtime.
explicit CallData(
rpc::v1::XRPLedgerAPIService::AsyncService& service,
grpc::ServerCompletionQueue& cq,
Application& app,
BindListener<Request, Response> bindListener,
Handler<Request, Response> handler,
RPC::Condition requiredCondition,
Resource::Charge loadType);
CallData(const CallData&) = delete;
CallData&
operator=(const CallData&) = delete;
virtual void
process() override;
virtual bool
isFinished() override;
virtual void
abort() override;
std::shared_ptr<Processor>
clone() override;
private:
// process the request. Called inside the coroutine passed to JobQueue
void
process(std::shared_ptr<JobQueue::Coro> coro);
// return load type of this RPC
Resource::Charge
getLoadType();
// return the Role required for this RPC
// for now, we are only supporting RPC's that require Role::USER for
// gRPC
Role
getRole();
// register endpoint with ResourceManager and return usage
Resource::Consumer
getUsage();
}; // CallData
}; // GRPCServerImpl
class GRPCServer
{
public:
explicit GRPCServer(Application& app) : impl_(app){};
GRPCServer(const GRPCServer&) = delete;
GRPCServer&
operator=(const GRPCServer&) = delete;
void
run();
~GRPCServer();
private:
GRPCServerImpl impl_;
std::thread thread_;
bool running_;
};
} // namespace ripple
#endif

View File

@@ -27,7 +27,8 @@ namespace ripple {
Message::Message (::google::protobuf::Message const& message, int type)
: mCategory(TrafficCount::categorize(message, type, false))
{
unsigned const messageBytes = message.ByteSize ();
auto const messageBytes = message.ByteSizeLong();
assert (messageBytes != 0);
/** Number of bytes in a message header. */

View File

@@ -0,0 +1,81 @@
syntax = "proto3";
package rpc.v1;
import "rpc/v1/ledger_objects.proto";
import "rpc/v1/amount.proto";
// A request to get info about an account.
message GetAccountInfoRequest {
// The address to get info about.
AccountAddress account = 1;
bool strict = 2;
LedgerSpecifier ledger = 3;
bool queue = 4;
bool signer_lists = 5;
}
message LedgerSpecifier {
enum Shortcut {
SHORTCUT_UNSPECIFIED = 0;
SHORTCUT_VALIDATED = 1;
SHORTCUT_CLOSED = 2;
SHORTCUT_CURRENT = 3;
}
oneof ledger {
Shortcut shortcut = 1;
uint32 sequence = 2;
// 32 bytes
bytes hash = 3;
}
}
// Response to GetAccountInfo RPC
message GetAccountInfoResponse {
AccountRoot account_data = 1;
SignerList signer_list = 2;
uint32 ledger_index = 3;
QueueData queue_data = 4;
bool validated = 5;
}
// Aggregate data about queued transactions
message QueueData {
uint32 txn_count = 1;
bool auth_change_queued = 2;
uint32 lowest_sequence = 3;
uint32 highest_sequence = 4;
XRPDropsAmount max_spend_drops_total = 5;
repeated QueuedTransaction transactions = 6;
}
// Data about a single queued transaction
message QueuedTransaction {
bool auth_change = 1;
XRPDropsAmount fee = 2;
uint64 fee_level = 3;
XRPDropsAmount max_spend_drops = 4;
uint32 sequence = 5;
uint32 last_ledger_sequence = 6;
}

View File

@@ -0,0 +1,44 @@
syntax = "proto3";
package rpc.v1;
message CurrencyAmount {
oneof amount {
XRPDropsAmount xrp_amount = 1;
IssuedCurrencyAmount issued_currency_amount = 2;
}
}
// A representation of an amount of XRP.
message XRPDropsAmount {
uint64 drops = 1;
}
// A representation of an account address
message AccountAddress {
//base58 encoding of an account
string address = 1;
}
// A representation of an amount of issued currency.
message IssuedCurrencyAmount {
// The currency used to value the amount.
Currency currency = 1;
// The value of the amount. 8 bytes
string value = 2;
// Unique account address of the entity issuing the currency.
AccountAddress issuer = 3;
}
message Currency {
// 3 character ASCII code
string name = 1;
// 160 bit currency code. 20 bytes
bytes code = 2;
}

View File

@@ -0,0 +1,51 @@
syntax = "proto3";
package rpc.v1;
import "rpc/v1/amount.proto";
// A request for the current transaction fee on the ledger.
message GetFeeRequest {
}
// Response to a GetFee RPC
message GetFeeResponse {
uint64 current_ledger_size = 1;
uint64 current_queue_size = 2;
Fee drops = 3;
uint64 expected_ledger_size = 4;
uint32 ledger_current_index = 5;
FeeLevels levels = 6;
uint64 max_queue_size = 7;
}
message Fee {
XRPDropsAmount base_fee = 1;
XRPDropsAmount median_fee = 2;
XRPDropsAmount minimum_fee = 3;
XRPDropsAmount open_ledger_fee = 4;
}
message FeeLevels {
uint64 median_level = 1;
uint64 minimum_level = 2;
uint64 open_ledger_level = 3;
uint64 reference_level = 4;
}

View File

@@ -0,0 +1,175 @@
syntax = "proto3";
package rpc.v1;
import "rpc/v1/amount.proto";
message LedgerObject {
oneof object {
AccountRoot account_root = 1;
RippleState ripple_state = 2;
Offer offer = 3;
SignerList signer_list = 4;
DirectoryNode directory_node = 5;
}
}
enum LedgerEntryType {
LEDGER_ENTRY_TYPE_UNSPECIFIED = 0;
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT = 1;
LEDGER_ENTRY_TYPE_AMENDMENTS = 2;
LEDGER_ENTRY_TYPE_CHECK = 3;
LEDGER_ENTRY_TYPE_DEPOSIT_PREAUTH = 4;
LEDGER_ENTRY_TYPE_DIRECTORY_NODE = 5;
LEDGER_ENTRY_TYPE_ESCROW = 6;
LEDGER_ENTRY_TYPE_FEE_SETTINGS = 7;
LEDGER_ENTRY_TYPE_LEDGER_HASHES = 8;
LEDGER_ENTRY_TYPE_OFFER = 9;
LEDGER_ENTRY_TYPE_PAY_CHANNEL = 10;
LEDGER_ENTRY_TYPE_RIPPLE_STATE = 11;
LEDGER_ENTRY_TYPE_SIGNER_LIST = 12;
}
message DirectoryNode {
uint32 flags = 1;
// 32 bytes
bytes root_index = 2;
repeated bytes indexes = 3;
uint64 index_next = 4;
uint64 index_previous = 5;
string owner = 6;
Currency taker_pays_currency = 7;
// 20 bytes
bytes taker_pays_issuer = 8;
Currency taker_gets_currency = 9;
// 20 bytes
bytes taker_gets_issuer = 10;
}
message SignerList {
uint32 flags = 1;
// 32 bytes
bytes previous_txn_id = 2;
uint32 previous_transaction_ledger_sequence = 3;
uint64 owner_node = 4;
repeated SignerEntry signer_entries = 5;
uint32 signer_list_id = 6;
uint32 signer_quorum = 7;
}
message SignerEntry {
AccountAddress account = 1;
// this is actually uint16, but protobuf can't express uint16
uint32 signer_weight = 2;
}
message AccountRoot {
AccountAddress account = 1;
XRPDropsAmount balance = 2;
uint32 sequence = 3;
uint32 flags = 4;
uint32 owner_count = 5;
// 32 bytes
bytes previous_transaction_id = 6;
uint32 previous_transaction_ledger_sequence = 7;
// 32 bytes
bytes account_transaction_id = 8;
// Variable length
bytes domain = 9;
// 16 bytes
bytes email_hash = 10;
// Variable length
bytes message_key = 11;
// base58 encoding
string regular_key = 12;
uint32 tick_size = 13;
uint32 transfer_rate = 14;
}
message RippleState {
CurrencyAmount balance = 1;
uint32 flags = 2;
CurrencyAmount low_limit = 3;
CurrencyAmount high_limit = 4;
uint64 low_node = 5;
uint64 high_node = 6;
uint32 low_quality_in = 7;
uint32 low_quality_out = 8;
uint32 high_quality_in = 9;
uint32 high_quality_out = 10;
// 32 bytes
bytes previous_transaction_id = 11;
uint32 previous_transaction_ledger_sequence = 12;
}
message Offer {
string account = 1;
uint32 sequence = 2;
uint32 flags = 3;
CurrencyAmount taker_pays = 4;
CurrencyAmount taker_gets = 5;
bytes book_directory = 6;
uint64 book_node = 7;
uint64 owner_node = 8;
uint32 expiration = 9;
// 32 bytes
bytes previous_transaction_id = 10;
uint32 previous_transaction_ledger_sequence = 11;
}

View File

@@ -0,0 +1,82 @@
syntax = "proto3";
package rpc.v1;
import "rpc/v1/amount.proto";
import "rpc/v1/ledger_objects.proto";
message Meta {
// index in ledger
uint64 transaction_index = 1;
// result code indicating whether the transaction succeeded or failed
TransactionResult transaction_result = 2;
repeated AffectedNode affected_nodes = 3;
CurrencyAmount delivered_amount = 4;
}
message TransactionResult {
enum ResultType {
RESULT_TYPE_UNSPECIFIED = 0;
// Claimed cost only
RESULT_TYPE_TEC = 1;
// Failure
RESULT_TYPE_TEF = 2;
// Local error
RESULT_TYPE_TEL = 3;
// Malformed transaction
RESULT_TYPE_TEM = 4;
// Retry
RESULT_TYPE_TER = 5;
// Success
RESULT_TYPE_TES = 6;
}
// category of the transaction result
ResultType result_type = 1;
// full result string, i.e. tesSUCCESS
string result = 2;
}
message AffectedNode {
LedgerEntryType ledger_entry_type = 1;
// 32 bytes
bytes ledger_index = 2;
oneof node {
CreatedNode created_node = 3;
DeletedNode deleted_node = 4;
ModifiedNode modified_node = 5;
}
}
message CreatedNode {
LedgerObject new_fields = 1;
}
message DeletedNode {
LedgerObject final_fields = 1;
}
message ModifiedNode {
LedgerObject final_fields = 1;
LedgerObject previous_fields = 2;
// 32 bytes
bytes previous_transaction_id = 3;
uint32 previous_transaction_ledger_sequence = 4;
}

View File

@@ -0,0 +1,28 @@
syntax = "proto3";
package rpc.v1;
import "rpc/v1/meta.proto";
// A request to submit the signed transaction to the ledger.
message SubmitTransactionRequest {
// The signed transaction to submit.
bytes signed_transaction = 1;
bool fail_hard = 2;
}
// A response when a signed transaction is submitted to the ledger.
message SubmitTransactionResponse {
// Code indicating the preliminary result of the transaction.
TransactionResult engine_result = 1;
// Numeric code indicating the preliminary result of the transaction, directly correlated to engine_result.
int64 engine_result_code = 2;
// Human-readable explanation of the transaction's preliminary result.
string engine_result_message = 3;
// 32 bytes
bytes hash = 4;
}

View File

@@ -0,0 +1,96 @@
syntax = "proto3";
package rpc.v1;
import "rpc/v1/amount.proto";
// A class encompassing all transactions.
message Transaction {
// The account originating the transaction.
AccountAddress account = 1;
// The fee attached to the transaction.
XRPDropsAmount fee = 2;
// The sequence number for the transaction.
uint32 sequence = 3;
// Data specific to a the type of transaction being submitted.
oneof transaction_data {
Payment payment = 4;
}
// Public key of the account which signed the transaction. Variable length
bytes signing_public_key = 5;
// Variable length
bytes signature = 6;
uint32 flags = 7;
uint32 last_ledger_sequence = 8;
uint32 source_tag = 9;
repeated Memo memos = 10;
repeated Signer signers = 11;
bytes account_transaction_id = 12;
}
message Memo {
// Variable length
bytes memo_data = 1;
// Variable length
bytes memo_format = 2;
// Variable length
bytes memo_type = 3;
}
message Signer {
AccountAddress account = 1;
// Variable length
bytes transaction_signature = 2;
// Variable length
bytes signing_public_key = 3;
}
message Payment {
// The amount of currency to pay, in either issued currency or XRP.
CurrencyAmount amount = 1;
// The destination of the payment.
AccountAddress destination = 2;
uint32 destination_tag = 3;
// 32 bytes
bytes invoice_id = 4;
repeated Path paths = 5;
CurrencyAmount send_max = 6;
CurrencyAmount deliver_min = 7;
}
message Path {
repeated PathElement elements = 1;
}
message PathElement {
AccountAddress account = 1;
Currency currency = 2;
AccountAddress issuer = 3;
}

View File

@@ -0,0 +1,40 @@
syntax = "proto3";
package rpc.v1;
import "rpc/v1/transaction.proto";
import "rpc/v1/meta.proto";
message GetTxRequest {
// hash of the transaction. 32 bytes
bytes hash = 1;
// if true, return data in binary format
bool binary = 2;
}
message GetTxResponse {
// The actual transaction
oneof serialized_transaction {
Transaction transaction = 1;
// Variable length
bytes transaction_binary = 2;
};
// Sequence number of ledger that contains this transaction
uint32 ledger_index = 3;
// 32 bytes
bytes hash = 4;
// whether the ledger has been validated
bool validated = 5;
// metadata about the transaction
oneof serialized_meta {
Meta meta = 6;
// Variable length
bytes meta_binary = 7;
}
}

View File

@@ -0,0 +1,25 @@
syntax = "proto3";
package rpc.v1;
import "rpc/v1/account_info.proto";
import "rpc/v1/fee.proto";
import "rpc/v1/submit.proto";
import "rpc/v1/tx.proto";
// RPCs available to interact with the XRP Ledger.
service XRPLedgerAPIService {
// Get account info for an account on the XRP Ledger.
rpc GetAccountInfo (GetAccountInfoRequest) returns (GetAccountInfoResponse);
// Get the fee for a transaction on the XRP Ledger.
rpc GetFee (GetFeeRequest) returns (GetFeeResponse);
// Submit a signed transaction to the XRP Ledger.
rpc SubmitTransaction (SubmitTransactionRequest) returns (SubmitTransactionResponse);
// Get the status of a transaction
rpc GetTx(GetTxRequest) returns (GetTxResponse);
}

View File

@@ -37,6 +37,19 @@ namespace RPC {
/** The context of information needed to call an RPC. */
struct Context
{
beast::Journal const j;
Application& app;
Resource::Charge& loadType;
NetworkOPs& netOps;
LedgerMaster& ledgerMaster;
Resource::Consumer& consumer;
Role role;
std::shared_ptr<JobQueue::Coro> coro{};
InfoSub::pointer infoSub{};
};
struct JsonContext : public Context
{
/**
* Data passed in from HTTP headers.
@@ -47,20 +60,18 @@ struct Context
boost::string_view forwardedFor;
};
beast::Journal const j;
Json::Value params;
Application& app;
Resource::Charge& loadType;
NetworkOPs& netOps;
LedgerMaster& ledgerMaster;
Resource::Consumer& consumer;
Role role;
unsigned int apiVersion;
std::shared_ptr<JobQueue::Coro> coro {};
InfoSub::pointer infoSub {};
Headers headers {};
};
template <class RequestType>
struct GRPCContext : public Context
{
RequestType params;
};
} // RPC
} // ripple

View File

@@ -21,6 +21,7 @@
#define RIPPLE_RPC_DELIVEREDAMOUNT_H_INCLUDED
#include <memory>
#include <rpc/v1/amount.pb.h>
namespace Json {
class Value;
@@ -35,6 +36,8 @@ class STTx;
namespace RPC {
struct JsonContext;
struct Context;
/**
@@ -56,6 +59,13 @@ insertDeliveredAmount(
void
insertDeliveredAmount(
Json::Value& meta,
JsonContext&,
std::shared_ptr<Transaction>,
TxMeta const&);
void
insertDeliveredAmount(
rpc::v1::CurrencyAmount& proto,
Context&,
std::shared_ptr<Transaction>,
TxMeta const&);

View File

@@ -0,0 +1,52 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_RPC_GRPCHANDLER_H_INCLUDED
#define RIPPLE_RPC_GRPCHANDLER_H_INCLUDED
#include <ripple/rpc/Context.h>
#include <grpcpp/grpcpp.h>
#include <rpc/v1/xrp_ledger.pb.h>
namespace ripple {
/*
* These handlers are for gRPC. They each take in a protobuf message that is
* nested inside RPC::GRPCContext<T>, where T is the request type
* The return value is the response type, as well as a status
* If the status is not Status::OK (meaning an error occurred), then only
* the status will be sent to the client, and the response will be ommitted
*/
std::pair<rpc::v1::GetAccountInfoResponse, grpc::Status>
doAccountInfoGrpc(RPC::GRPCContext<rpc::v1::GetAccountInfoRequest>& context);
std::pair<rpc::v1::GetFeeResponse, grpc::Status>
doFeeGrpc(RPC::GRPCContext<rpc::v1::GetFeeRequest>& context);
std::pair<rpc::v1::SubmitTransactionResponse, grpc::Status>
doSubmitGrpc(RPC::GRPCContext<rpc::v1::SubmitTransactionRequest>& context);
// NOTE, this only supports Payment transactions at this time
std::pair<rpc::v1::GetTxResponse, grpc::Status>
doTxGrpc(RPC::GRPCContext<rpc::v1::GetTxRequest>& context);
} // namespace ripple
#endif

View File

@@ -28,10 +28,10 @@
namespace ripple {
namespace RPC {
struct Context;
struct JsonContext;
/** Execute an RPC command and store the results in a Json::Value. */
Status doCommand (RPC::Context&, Json::Value&);
Status doCommand (RPC::JsonContext&, Json::Value&);
Role roleRequired (unsigned int version, std::string const& method );

View File

@@ -63,7 +63,7 @@ void addChannel (Json::Value& jsonLines, SLE const& line)
// limit: integer // optional
// marker: opaque // optional, resume previous query
// }
Json::Value doAccountChannels (RPC::Context& context)
Json::Value doAccountChannels (RPC::JsonContext& context)
{
auto const& params (context.params);
if (! params.isMember (jss::account))

View File

@@ -28,7 +28,7 @@
namespace ripple {
Json::Value doAccountCurrencies (RPC::Context& context)
Json::Value doAccountCurrencies (RPC::JsonContext& context)
{
auto& params = context.params;

View File

@@ -17,7 +17,6 @@
*/
//==============================================================================
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/TxQ.h>
#include <ripple/json/json_value.h>
@@ -27,7 +26,9 @@
#include <ripple/protocol/jss.h>
#include <ripple/protocol/UintTypes.h>
#include <ripple/rpc/Context.h>
#include <ripple/rpc/GRPCHandlers.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <grpc/status.h>
namespace ripple {
@@ -47,7 +48,7 @@ namespace ripple {
// }
// TODO(tom): what is that "default"?
Json::Value doAccountInfo (RPC::Context& context)
Json::Value doAccountInfo (RPC::JsonContext& context)
{
auto& params = context.params;
@@ -183,4 +184,93 @@ Json::Value doAccountInfo (RPC::Context& context)
return result;
}
} // ripple
std::pair<rpc::v1::GetAccountInfoResponse, grpc::Status>
doAccountInfoGrpc(RPC::GRPCContext<rpc::v1::GetAccountInfoRequest>& context)
{
// Return values
rpc::v1::GetAccountInfoResponse result;
grpc::Status status = grpc::Status::OK;
// input
rpc::v1::GetAccountInfoRequest& params = context.params;
// get ledger
std::shared_ptr<ReadView const> ledger;
auto lgrStatus = RPC::ledgerFromRequest(ledger, context);
if (lgrStatus || !ledger)
{
grpc::Status errorStatus;
if (lgrStatus.toErrorCode() == rpcINVALID_PARAMS)
{
errorStatus = grpc::Status(
grpc::StatusCode::INVALID_ARGUMENT, lgrStatus.message());
}
else
{
errorStatus =
grpc::Status(grpc::StatusCode::NOT_FOUND, lgrStatus.message());
}
return {result, errorStatus};
}
result.set_ledger_index(ledger->info().seq);
result.set_validated(
RPC::isValidated(context.ledgerMaster, *ledger, context.app));
// decode account
AccountID accountID;
std::string strIdent = params.account().address();
error_code_i code =
RPC::accountFromStringWithCode(accountID, strIdent, params.strict());
if (code != rpcSUCCESS)
{
grpc::Status errorStatus{grpc::StatusCode::INVALID_ARGUMENT,
"invalid account"};
return {result, errorStatus};
}
// get account data
auto const sleAccepted = ledger->read(keylet::account(accountID));
if (sleAccepted)
{
RPC::populateAccountRoot(*result.mutable_account_data(), *sleAccepted);
// signer lists
if (params.signer_lists())
{
auto const sleSigners = ledger->read(keylet::signers(accountID));
if (sleSigners)
{
rpc::v1::SignerList& signerListProto =
*result.mutable_signer_list();
RPC::populateSignerList(signerListProto, *sleSigners);
}
}
// queued transactions
if (params.queue())
{
if (!ledger->open())
{
grpc::Status errorStatus{
grpc::StatusCode::INVALID_ARGUMENT,
"requested queue but ledger is not open"};
return {result, errorStatus};
}
auto const txs =
context.app.getTxQ().getAccountTxs(accountID, *ledger);
rpc::v1::QueueData& queueData = *result.mutable_queue_data();
RPC::populateQueueData(queueData, txs);
}
}
else
{
grpc::Status errorStatus{grpc::StatusCode::NOT_FOUND,
"account not found"};
return {result, errorStatus};
}
return {result, status};
}
} // namespace ripple

View File

@@ -78,7 +78,7 @@ void addLine (Json::Value& jsonLines, RippleState const& line)
// limit: integer // optional
// marker: opaque // optional, resume previous query
// }
Json::Value doAccountLines (RPC::Context& context)
Json::Value doAccountLines (RPC::JsonContext& context)
{
auto const& params (context.params);
if (! params.isMember (jss::account))

View File

@@ -46,7 +46,7 @@ namespace ripple {
}
*/
Json::Value doAccountObjects (RPC::Context& context)
Json::Value doAccountObjects (RPC::JsonContext& context)
{
auto const& params = context.params;
if (! params.isMember (jss::account))

View File

@@ -53,7 +53,7 @@ void appendOfferJson (std::shared_ptr<SLE const> const& offer,
// limit: integer // optional
// marker: opaque // optional, resume previous query
// }
Json::Value doAccountOffers (RPC::Context& context)
Json::Value doAccountOffers (RPC::JsonContext& context)
{
auto const& params (context.params);
if (! params.isMember (jss::account))

View File

@@ -44,7 +44,7 @@ namespace ripple {
// limit: integer, // optional
// marker: opaque // optional, resume previous query
// }
Json::Value doAccountTx (RPC::Context& context)
Json::Value doAccountTx (RPC::JsonContext& context)
{
auto& params = context.params;

View File

@@ -43,7 +43,7 @@ namespace ripple {
// offset: integer, // optional, defaults to 0
// limit: integer // optional
// }
Json::Value doAccountTxOld (RPC::Context& context)
Json::Value doAccountTxOld (RPC::JsonContext& context)
{
std::uint32_t offset
= context.params.isMember (jss::offset)

View File

@@ -25,11 +25,11 @@
namespace ripple {
Json::Value doAccountTxOld (RPC::Context& context);
Json::Value doAccountTx (RPC::Context& context);
Json::Value doAccountTxOld (RPC::JsonContext& context);
Json::Value doAccountTx (RPC::JsonContext& context);
// Temporary switching code until the old account_tx is removed
Json::Value doAccountTxSwitch (RPC::Context& context)
Json::Value doAccountTxSwitch (RPC::JsonContext& context)
{
if (context.params.isMember(jss::offset) ||
context.params.isMember(jss::count) ||

View File

@@ -24,7 +24,7 @@
namespace ripple {
Json::Value doBlackList (RPC::Context& context)
Json::Value doBlackList (RPC::JsonContext& context)
{
auto& rm = context.app.getResourceManager();
if (context.params.isMember(jss::threshold))

View File

@@ -31,7 +31,7 @@
namespace ripple {
Json::Value doBookOffers (RPC::Context& context)
Json::Value doBookOffers (RPC::JsonContext& context)
{
// VFALCO TODO Here is a terrible place for this kind of business
// logic. It needs to be moved elsewhere and documented,

View File

@@ -30,7 +30,7 @@
namespace ripple {
// can_delete [<ledgerid>|<ledgerhash>|now|always|never]
Json::Value doCanDelete (RPC::Context& context)
Json::Value doCanDelete (RPC::JsonContext& context)
{
if (! context.app.getSHAMapStore().advisoryDelete())
return RPC::make_error(rpcNOT_ENABLED);

View File

@@ -33,7 +33,7 @@ namespace ripple {
// port: <number>
// }
// XXX Might allow domain for manual connections.
Json::Value doConnect (RPC::Context& context)
Json::Value doConnect (RPC::JsonContext& context)
{
if (context.app.config().standalone())
return "cannot connect in standalone mode";

View File

@@ -25,7 +25,7 @@
namespace ripple {
Json::Value doConsensusInfo (RPC::Context& context)
Json::Value doConsensusInfo (RPC::JsonContext& context)
{
Json::Value ret (Json::objectValue);

View File

@@ -40,7 +40,7 @@ namespace ripple {
}
*/
Json::Value
doCrawlShards(RPC::Context& context)
doCrawlShards(RPC::JsonContext& context)
{
if (context.role != Role::ADMIN)
return rpcError(rpcNO_PERMISSION);

View File

@@ -34,7 +34,7 @@ namespace ripple {
// ledger_index : <ledger_index>
// }
Json::Value doDepositAuthorized (RPC::Context& context)
Json::Value doDepositAuthorized (RPC::JsonContext& context)
{
Json::Value const& params = context.params;

View File

@@ -47,7 +47,7 @@ namespace ripple {
}
*/
Json::Value
doDownloadShard(RPC::Context& context)
doDownloadShard(RPC::JsonContext& context)
{
if (context.role != Role::ADMIN)
return rpcError(rpcNO_PERMISSION);

View File

@@ -33,7 +33,7 @@ namespace ripple {
// feature : <feature>
// vetoed : true/false
// }
Json::Value doFeature (RPC::Context& context)
Json::Value doFeature (RPC::JsonContext& context)
{
// Get majority amendment status

View File

@@ -21,12 +21,14 @@
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/TxQ.h>
#include <ripple/rpc/Context.h>
#include <ripple/rpc/GRPCHandlers.h>
#include <ripple/protocol/ErrorCodes.h>
#include <ripple/protocol/Feature.h>
#include <ripple/basics/mulDiv.h>
namespace ripple
{
Json::Value doFee(RPC::Context& context)
Json::Value doFee(RPC::JsonContext& context)
{
auto result = context.app.getTxQ().doRPC(context.app);
if (result.type() == Json::objectValue)
@@ -35,4 +37,51 @@ namespace ripple
RPC::inject_error(rpcINTERNAL, context.params);
return context.params;
}
} // ripple
std::pair<rpc::v1::GetFeeResponse, grpc::Status>
doFeeGrpc(RPC::GRPCContext<rpc::v1::GetFeeRequest>& context)
{
rpc::v1::GetFeeResponse reply;
grpc::Status status = grpc::Status::OK;
Application& app = context.app;
auto const view = app.openLedger().current();
if (!view)
{
BOOST_ASSERT(false);
return {reply, status};
}
auto const metrics = app.getTxQ().getMetrics(*view);
// current ledger data
reply.set_current_ledger_size(metrics.txInLedger);
reply.set_current_queue_size(metrics.txCount);
reply.set_expected_ledger_size(metrics.txPerLedger);
reply.set_ledger_current_index(view->info().seq);
reply.set_max_queue_size(*metrics.txQMaxSize);
// fee levels data
rpc::v1::FeeLevels& levels = *reply.mutable_levels();
levels.set_median_level(metrics.medFeeLevel.fee());
levels.set_minimum_level(metrics.minProcessingFeeLevel.fee());
levels.set_open_ledger_level(metrics.openLedgerFeeLevel.fee());
levels.set_reference_level(metrics.referenceFeeLevel.fee());
// fee data
rpc::v1::Fee& drops = *reply.mutable_drops();
auto const baseFee = view->fees().base;
drops.mutable_base_fee()->set_drops(
toDrops(metrics.referenceFeeLevel, baseFee).second.drops());
drops.mutable_minimum_fee()->set_drops(
toDrops(metrics.minProcessingFeeLevel, baseFee).second.drops());
drops.mutable_median_fee()->set_drops(
toDrops(metrics.medFeeLevel, baseFee).second.drops());
drops.mutable_open_ledger_fee()->set_drops(
(toDrops(metrics.openLedgerFeeLevel - FeeLevel64{1}, baseFee).second +
1)
.drops());
return {reply, status};
}
} // namespace ripple

View File

@@ -24,7 +24,7 @@
namespace ripple {
Json::Value doFetchInfo (RPC::Context& context)
Json::Value doFetchInfo (RPC::JsonContext& context)
{
Json::Value ret (Json::objectValue);

View File

@@ -49,7 +49,7 @@ namespace ripple {
// gateway_balances [<ledger>] <account> [<howallet> [<hotwallet [...
Json::Value doGatewayBalances (RPC::Context& context)
Json::Value doGatewayBalances (RPC::JsonContext& context)
{
auto& params = context.params;

View File

@@ -143,7 +143,7 @@ Json::Value getCountsJson(Application& app, int minObjectCount)
// {
// min_count: <number> // optional, defaults to 10
// }
Json::Value doGetCounts (RPC::Context& context)
Json::Value doGetCounts (RPC::JsonContext& context)
{
int minCount = 10;

View File

@@ -24,68 +24,68 @@
namespace ripple {
Json::Value doAccountCurrencies (RPC::Context&);
Json::Value doAccountInfo (RPC::Context&);
Json::Value doAccountLines (RPC::Context&);
Json::Value doAccountChannels (RPC::Context&);
Json::Value doAccountObjects (RPC::Context&);
Json::Value doAccountOffers (RPC::Context&);
Json::Value doAccountTx (RPC::Context&);
Json::Value doAccountTxSwitch (RPC::Context&);
Json::Value doAccountTxOld (RPC::Context&);
Json::Value doBookOffers (RPC::Context&);
Json::Value doBlackList (RPC::Context&);
Json::Value doCanDelete (RPC::Context&);
Json::Value doChannelAuthorize (RPC::Context&);
Json::Value doChannelVerify (RPC::Context&);
Json::Value doConnect (RPC::Context&);
Json::Value doConsensusInfo (RPC::Context&);
Json::Value doDepositAuthorized (RPC::Context&);
Json::Value doDownloadShard (RPC::Context&);
Json::Value doFeature (RPC::Context&);
Json::Value doFee (RPC::Context&);
Json::Value doFetchInfo (RPC::Context&);
Json::Value doGatewayBalances (RPC::Context&);
Json::Value doGetCounts (RPC::Context&);
Json::Value doLedgerAccept (RPC::Context&);
Json::Value doLedgerCleaner (RPC::Context&);
Json::Value doLedgerClosed (RPC::Context&);
Json::Value doLedgerCurrent (RPC::Context&);
Json::Value doLedgerData (RPC::Context&);
Json::Value doLedgerEntry (RPC::Context&);
Json::Value doLedgerHeader (RPC::Context&);
Json::Value doLedgerRequest (RPC::Context&);
Json::Value doLogLevel (RPC::Context&);
Json::Value doLogRotate (RPC::Context&);
Json::Value doNoRippleCheck (RPC::Context&);
Json::Value doOwnerInfo (RPC::Context&);
Json::Value doPathFind (RPC::Context&);
Json::Value doPeers (RPC::Context&);
Json::Value doPing (RPC::Context&);
Json::Value doPrint (RPC::Context&);
Json::Value doRandom (RPC::Context&);
Json::Value doPeerReservationsAdd (RPC::Context&);
Json::Value doPeerReservationsDel (RPC::Context&);
Json::Value doPeerReservationsList (RPC::Context&);
Json::Value doRipplePathFind (RPC::Context&);
Json::Value doServerInfo (RPC::Context&); // for humans
Json::Value doServerState (RPC::Context&); // for machines
Json::Value doSign (RPC::Context&);
Json::Value doSignFor (RPC::Context&);
Json::Value doCrawlShards (RPC::Context&);
Json::Value doStop (RPC::Context&);
Json::Value doSubmit (RPC::Context&);
Json::Value doSubmitMultiSigned (RPC::Context&);
Json::Value doSubscribe (RPC::Context&);
Json::Value doTransactionEntry (RPC::Context&);
Json::Value doTx (RPC::Context&);
Json::Value doTxHistory (RPC::Context&);
Json::Value doUnlList (RPC::Context&);
Json::Value doUnsubscribe (RPC::Context&);
Json::Value doValidationCreate (RPC::Context&);
Json::Value doWalletPropose (RPC::Context&);
Json::Value doValidators (RPC::Context&);
Json::Value doValidatorListSites (RPC::Context&);
Json::Value doAccountCurrencies (RPC::JsonContext&);
Json::Value doAccountInfo (RPC::JsonContext&);
Json::Value doAccountLines (RPC::JsonContext&);
Json::Value doAccountChannels (RPC::JsonContext&);
Json::Value doAccountObjects (RPC::JsonContext&);
Json::Value doAccountOffers (RPC::JsonContext&);
Json::Value doAccountTx (RPC::JsonContext&);
Json::Value doAccountTxSwitch (RPC::JsonContext&);
Json::Value doAccountTxOld (RPC::JsonContext&);
Json::Value doBookOffers (RPC::JsonContext&);
Json::Value doBlackList (RPC::JsonContext&);
Json::Value doCanDelete (RPC::JsonContext&);
Json::Value doChannelAuthorize (RPC::JsonContext&);
Json::Value doChannelVerify (RPC::JsonContext&);
Json::Value doConnect (RPC::JsonContext&);
Json::Value doConsensusInfo (RPC::JsonContext&);
Json::Value doDepositAuthorized (RPC::JsonContext&);
Json::Value doDownloadShard (RPC::JsonContext&);
Json::Value doFeature (RPC::JsonContext&);
Json::Value doFee (RPC::JsonContext&);
Json::Value doFetchInfo (RPC::JsonContext&);
Json::Value doGatewayBalances (RPC::JsonContext&);
Json::Value doGetCounts (RPC::JsonContext&);
Json::Value doLedgerAccept (RPC::JsonContext&);
Json::Value doLedgerCleaner (RPC::JsonContext&);
Json::Value doLedgerClosed (RPC::JsonContext&);
Json::Value doLedgerCurrent (RPC::JsonContext&);
Json::Value doLedgerData (RPC::JsonContext&);
Json::Value doLedgerEntry (RPC::JsonContext&);
Json::Value doLedgerHeader (RPC::JsonContext&);
Json::Value doLedgerRequest (RPC::JsonContext&);
Json::Value doLogLevel (RPC::JsonContext&);
Json::Value doLogRotate (RPC::JsonContext&);
Json::Value doNoRippleCheck (RPC::JsonContext&);
Json::Value doOwnerInfo (RPC::JsonContext&);
Json::Value doPathFind (RPC::JsonContext&);
Json::Value doPeers (RPC::JsonContext&);
Json::Value doPing (RPC::JsonContext&);
Json::Value doPrint (RPC::JsonContext&);
Json::Value doRandom (RPC::JsonContext&);
Json::Value doPeerReservationsAdd (RPC::JsonContext&);
Json::Value doPeerReservationsDel (RPC::JsonContext&);
Json::Value doPeerReservationsList (RPC::JsonContext&);
Json::Value doRipplePathFind (RPC::JsonContext&);
Json::Value doServerInfo (RPC::JsonContext&); // for humans
Json::Value doServerState (RPC::JsonContext&); // for machines
Json::Value doSign (RPC::JsonContext&);
Json::Value doSignFor (RPC::JsonContext&);
Json::Value doCrawlShards (RPC::JsonContext&);
Json::Value doStop (RPC::JsonContext&);
Json::Value doSubmit (RPC::JsonContext&);
Json::Value doSubmitMultiSigned (RPC::JsonContext&);
Json::Value doSubscribe (RPC::JsonContext&);
Json::Value doTransactionEntry (RPC::JsonContext&);
Json::Value doTx (RPC::JsonContext&);
Json::Value doTxHistory (RPC::JsonContext&);
Json::Value doUnlList (RPC::JsonContext&);
Json::Value doUnsubscribe (RPC::JsonContext&);
Json::Value doValidationCreate (RPC::JsonContext&);
Json::Value doWalletPropose (RPC::JsonContext&);
Json::Value doValidators (RPC::JsonContext&);
Json::Value doValidatorListSites (RPC::JsonContext&);
} // ripple
#endif

View File

@@ -31,7 +31,7 @@
namespace ripple {
Json::Value doLedgerAccept (RPC::Context& context)
Json::Value doLedgerAccept (RPC::JsonContext& context)
{
std::unique_lock lock{context.app.getMasterMutex()};
Json::Value jvResult;

View File

@@ -25,7 +25,7 @@
namespace ripple {
Json::Value doLedgerCleaner (RPC::Context& context)
Json::Value doLedgerCleaner (RPC::JsonContext& context)
{
context.app.getLedgerMaster().doLedgerCleaner (context.params);
return RPC::makeObjectValue ("Cleaner configured");

View File

@@ -25,7 +25,7 @@
namespace ripple {
Json::Value doLedgerClosed (RPC::Context& context)
Json::Value doLedgerClosed (RPC::JsonContext& context)
{
auto ledger = context.ledgerMaster.getClosedLedger ();
assert (ledger);

View File

@@ -26,7 +26,7 @@
namespace ripple {
Json::Value doLedgerCurrent (RPC::Context& context)
Json::Value doLedgerCurrent (RPC::JsonContext& context)
{
Json::Value jvResult;
jvResult[jss::ledger_current_index] =

View File

@@ -40,7 +40,7 @@ namespace ripple {
// ledger_index: chosen ledger's index
// state: array of state nodes
// marker: resume point, if any
Json::Value doLedgerData (RPC::Context& context)
Json::Value doLedgerData (RPC::JsonContext& context)
{
std::shared_ptr<ReadView const> lpLedger;
auto const& params = context.params;

View File

@@ -35,7 +35,7 @@ namespace ripple {
// ledger_index : <ledger_index>
// ...
// }
Json::Value doLedgerEntry (RPC::Context& context)
Json::Value doLedgerEntry (RPC::JsonContext& context)
{
std::shared_ptr<ReadView const> lpLedger;
auto jvResult = RPC::lookupLedger (lpLedger, context);

View File

@@ -31,7 +31,7 @@
namespace ripple {
namespace RPC {
LedgerHandler::LedgerHandler (Context& context) : context_ (context)
LedgerHandler::LedgerHandler (JsonContext& context) : context_ (context)
{
}

View File

@@ -38,7 +38,7 @@ class Object;
namespace ripple {
namespace RPC {
struct Context;
struct JsonContext;
// ledger [id|index|current|closed] [full]
// {
@@ -48,7 +48,7 @@ struct Context;
class LedgerHandler {
public:
explicit LedgerHandler (Context&);
explicit LedgerHandler (JsonContext&);
Status check ();
@@ -71,7 +71,7 @@ public:
}
private:
Context& context_;
JsonContext& context_;
std::shared_ptr<ReadView const> ledger_;
std::vector<TxQ::TxDetails> queueTxs_;
Json::Value result_;

View File

@@ -29,7 +29,7 @@ namespace ripple {
// ledger_hash : <ledger>
// ledger_index : <ledger_index>
// }
Json::Value doLedgerHeader (RPC::Context& context)
Json::Value doLedgerHeader (RPC::JsonContext& context)
{
std::shared_ptr<ReadView const> lpLedger;
auto jvResult = RPC::lookupLedger (lpLedger, context);

View File

@@ -34,7 +34,7 @@ namespace ripple {
// ledger_hash : <ledger>
// ledger_index : <ledger_index>
// }
Json::Value doLedgerRequest (RPC::Context& context)
Json::Value doLedgerRequest (RPC::JsonContext& context)
{
auto const hasHash = context.params.isMember (jss::ledger_hash);
auto const hasIndex = context.params.isMember (jss::ledger_index);

View File

@@ -28,7 +28,7 @@
namespace ripple {
Json::Value doLogLevel (RPC::Context& context)
Json::Value doLogLevel (RPC::JsonContext& context)
{
// log_level
if (!context.params.isMember (jss::severity))

View File

@@ -24,7 +24,7 @@
namespace ripple {
Json::Value doLogRotate (RPC::Context& context)
Json::Value doLogRotate (RPC::JsonContext& context)
{
context.app.getPerfLog().rotate();
return RPC::makeObjectValue (context.app.logs().rotate());

View File

@@ -32,7 +32,7 @@
namespace ripple {
static void fillTransaction (
RPC::Context& context,
RPC::JsonContext& context,
Json::Value& txArray,
AccountID const& accountID,
std::uint32_t& sequence,
@@ -55,7 +55,7 @@ static void fillTransaction (
// role: gateway|user // account role to assume
// transactions: true // optional, reccommend transactions
// }
Json::Value doNoRippleCheck (RPC::Context& context)
Json::Value doNoRippleCheck (RPC::JsonContext& context)
{
auto const& params (context.params);
if (! params.isMember (jss::account))

View File

@@ -30,7 +30,7 @@ namespace ripple {
// {
// 'ident' : <indent>,
// }
Json::Value doOwnerInfo (RPC::Context& context)
Json::Value doOwnerInfo (RPC::JsonContext& context)
{
if (!context.params.isMember (jss::account) &&
!context.params.isMember (jss::ident))

View File

@@ -29,7 +29,7 @@
namespace ripple {
Json::Value doPathFind (RPC::Context& context)
Json::Value doPathFind (RPC::JsonContext& context)
{
if (context.app.config().PATH_SEARCH_MAX == 0)
return rpcError (rpcNOT_SUPPORTED);

View File

@@ -40,7 +40,7 @@ namespace ripple {
// channel_id: 256-bit channel id
// drops: 64-bit uint (as string)
// }
Json::Value doChannelAuthorize (RPC::Context& context)
Json::Value doChannelAuthorize (RPC::JsonContext& context)
{
auto const& params (context.params);
for (auto const& p : {jss::channel_id, jss::amount})
@@ -94,7 +94,7 @@ Json::Value doChannelAuthorize (RPC::Context& context)
// drops: 64-bit uint (as string)
// signature: signature to verify
// }
Json::Value doChannelVerify (RPC::Context& context)
Json::Value doChannelVerify (RPC::JsonContext& context)
{
auto const& params (context.params);
for (auto const& p :

View File

@@ -27,7 +27,7 @@
namespace ripple {
Json::Value doPeers (RPC::Context& context)
Json::Value doPeers (RPC::JsonContext& context)
{
Json::Value jvResult (Json::objectValue);

View File

@@ -25,10 +25,10 @@
namespace ripple {
namespace RPC {
struct Context;
struct JsonContext;
} // RPC
Json::Value doPing (RPC::Context& context)
Json::Value doPing (RPC::JsonContext& context)
{
Json::Value ret(Json::objectValue);
switch (context.role)

View File

@@ -25,7 +25,7 @@
namespace ripple {
Json::Value doPrint (RPC::Context& context)
Json::Value doPrint (RPC::JsonContext& context)
{
JsonPropertyStream stream;
if (context.params.isObject()

View File

@@ -28,14 +28,14 @@
namespace ripple {
namespace RPC {
struct Context;
struct JsonContext;
}
// Result:
// {
// random: <uint256>
// }
Json::Value doRandom (RPC::Context& context)
Json::Value doRandom (RPC::JsonContext& context)
{
// TODO(tom): the try/catch is almost certainly redundant, we catch at the
// top level too.

View File

@@ -33,7 +33,7 @@
namespace ripple {
Json::Value
doPeerReservationsAdd(RPC::Context& context)
doPeerReservationsAdd(RPC::JsonContext& context)
{
auto const& params = context.params;
@@ -86,7 +86,7 @@ doPeerReservationsAdd(RPC::Context& context)
}
Json::Value
doPeerReservationsDel(RPC::Context& context)
doPeerReservationsDel(RPC::JsonContext& context)
{
auto const& params = context.params;
@@ -113,7 +113,7 @@ doPeerReservationsDel(RPC::Context& context)
}
Json::Value
doPeerReservationsList(RPC::Context& context)
doPeerReservationsList(RPC::JsonContext& context)
{
auto const& reservations = context.app.peerReservations().list();
// Enumerate the reservations in context.app.peerReservations()

View File

@@ -28,7 +28,7 @@
namespace ripple {
// This interface is deprecated.
Json::Value doRipplePathFind (RPC::Context& context)
Json::Value doRipplePathFind (RPC::JsonContext& context)
{
if (context.app.config().PATH_SEARCH_MAX == 0)
return rpcError (rpcNOT_SUPPORTED);

View File

@@ -27,7 +27,7 @@
namespace ripple {
Json::Value doServerInfo (RPC::Context& context)
Json::Value doServerInfo (RPC::JsonContext& context)
{
Json::Value ret (Json::objectValue);

View File

@@ -27,7 +27,7 @@
namespace ripple {
Json::Value doServerState (RPC::Context& context)
Json::Value doServerState (RPC::JsonContext& context)
{
Json::Value ret (Json::objectValue);

View File

@@ -31,7 +31,7 @@ namespace ripple {
// account: <signing account>
// secret: <secret of signing account>
// }
Json::Value doSignFor (RPC::Context& context)
Json::Value doSignFor (RPC::JsonContext& context)
{
if (context.role != Role::ADMIN && !context.app.config().canSign())
{

View File

@@ -29,7 +29,7 @@ namespace ripple {
// tx_json: <object>,
// secret: <secret>
// }
Json::Value doSign (RPC::Context& context)
Json::Value doSign (RPC::JsonContext& context)
{
if (context.role != Role::ADMIN && !context.app.config().canSign())
{

View File

@@ -26,10 +26,10 @@
namespace ripple {
namespace RPC {
struct Context;
struct JsonContext;
}
Json::Value doStop (RPC::Context& context)
Json::Value doStop (RPC::JsonContext& context)
{
std::unique_lock lock{context.app.getMasterMutex()};
context.app.signalStop ();

View File

@@ -26,10 +26,12 @@
#include <ripple/resource/Fees.h>
#include <ripple/rpc/Context.h>
#include <ripple/rpc/impl/TransactionSign.h>
#include <ripple/rpc/GRPCHandlers.h>
#include <ripple/rpc/impl/RPCHelpers.h>
namespace ripple {
static NetworkOPs::FailHard getFailHard (RPC::Context const& context)
static NetworkOPs::FailHard getFailHard (RPC::JsonContext const& context)
{
return NetworkOPs::doFailHard (
context.params.isMember ("fail_hard")
@@ -40,7 +42,7 @@ static NetworkOPs::FailHard getFailHard (RPC::Context const& context)
// tx_json: <object>,
// secret: <secret>
// }
Json::Value doSubmit (RPC::Context& context)
Json::Value doSubmit (RPC::JsonContext& context)
{
context.loadType = Resource::feeMediumBurdenRPC;
@@ -181,4 +183,100 @@ Json::Value doSubmit (RPC::Context& context)
}
}
} // ripple
std::pair<rpc::v1::SubmitTransactionResponse, grpc::Status>
doSubmitGrpc(RPC::GRPCContext<rpc::v1::SubmitTransactionRequest>& context)
{
// return values
rpc::v1::SubmitTransactionResponse result;
grpc::Status status = grpc::Status::OK;
// input
auto request = context.params;
std::string const& tx = request.signed_transaction();
// convert to blob
Blob blob{tx.begin(), tx.end()};
// serialize
SerialIter sitTrans(makeSlice(blob));
std::shared_ptr<STTx const> stpTrans;
try
{
stpTrans = std::make_shared<STTx const>(std::ref(sitTrans));
}
catch (std::exception& e)
{
grpc::Status errorStatus{
grpc::StatusCode::INVALID_ARGUMENT,
"invalid transaction: " + std::string(e.what())};
return {result, errorStatus};
}
// check validity
{
if (!context.app.checkSigs())
forceValidity(
context.app.getHashRouter(),
stpTrans->getTransactionID(),
Validity::SigGoodOnly);
auto [validity, reason] = checkValidity(
context.app.getHashRouter(),
*stpTrans,
context.ledgerMaster.getCurrentLedger()->rules(),
context.app.config());
if (validity != Validity::Valid)
{
grpc::Status errorStatus{grpc::StatusCode::INVALID_ARGUMENT,
"invalid transaction: " + reason};
return {result, errorStatus};
}
}
std::string reason;
auto tpTrans = std::make_shared<Transaction>(stpTrans, reason, context.app);
if (tpTrans->getStatus() != NEW)
{
grpc::Status errorStatus{grpc::StatusCode::INVALID_ARGUMENT,
"invalid transaction: " + reason};
return {result, errorStatus};
}
try
{
auto const failType = NetworkOPs::doFailHard(request.fail_hard());
// submit to network
context.netOps.processTransaction(
tpTrans, isUnlimited(context.role), true, failType);
}
catch (std::exception& e)
{
grpc::Status errorStatus{
grpc::StatusCode::INVALID_ARGUMENT,
"invalid transaction : " + std::string(e.what())};
return {result, errorStatus};
}
// return preliminary result
if (temUNCERTAIN != tpTrans->getResult())
{
RPC::populateTransactionResultType(
*result.mutable_engine_result(), tpTrans->getResult());
std::string sToken;
std::string sHuman;
transResultInfo(tpTrans->getResult(), sToken, sHuman);
result.mutable_engine_result()->set_result(sToken);
result.set_engine_result_code(TERtoInt(tpTrans->getResult()));
result.set_engine_result_message(sHuman);
uint256 hash = tpTrans->getID();
result.set_hash(hash.data(), hash.size());
}
return {result, status};
}
} // namespace ripple

View File

@@ -30,7 +30,7 @@ namespace ripple {
// SigningAccounts <array>,
// tx_json: <object>,
// }
Json::Value doSubmitMultiSigned (RPC::Context& context)
Json::Value doSubmitMultiSigned (RPC::JsonContext& context)
{
// Bail if multisign is not enabled.
if (! context.app.getLedgerMaster().getValidatedRules().

View File

@@ -33,7 +33,7 @@
namespace ripple {
Json::Value doSubscribe (RPC::Context& context)
Json::Value doSubscribe (RPC::JsonContext& context)
{
InfoSub::pointer ispSub;
Json::Value jvResult (Json::objectValue);

View File

@@ -32,7 +32,7 @@ namespace ripple {
//
// XXX In this case, not specify either ledger does not mean ledger current. It
// means any ledger.
Json::Value doTransactionEntry (RPC::Context& context)
Json::Value doTransactionEntry (RPC::JsonContext& context)
{
std::shared_ptr<ReadView const> lpLedger;
Json::Value jvResult = RPC::lookupLedger (lpLedger, context);

View File

@@ -27,6 +27,7 @@
#include <ripple/rpc/Context.h>
#include <ripple/rpc/DeliveredAmount.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <ripple/rpc/GRPCHandlers.h>
namespace ripple {
@@ -52,15 +53,22 @@ isHexTxID (std::string const& txid)
static
bool
isValidated (RPC::Context& context, std::uint32_t seq, uint256 const& hash)
isValidated(LedgerMaster& ledgerMaster, std::uint32_t seq, uint256 const& hash)
{
if (!context.ledgerMaster.haveLedger (seq))
if (!ledgerMaster.haveLedger (seq))
return false;
if (seq > context.ledgerMaster.getValidatedLedger ()->info().seq)
if (seq > ledgerMaster.getValidatedLedger ()->info().seq)
return false;
return context.ledgerMaster.getHashBySeq (seq) == hash;
return ledgerMaster.getHashBySeq (seq) == hash;
}
static
bool
isValidated (RPC::JsonContext& context, std::uint32_t seq, uint256 const& hash)
{
return isValidated(context.ledgerMaster, seq, hash);
}
bool
@@ -83,7 +91,7 @@ getMetaHex (Ledger const& ledger,
return true;
}
Json::Value doTx (RPC::Context& context)
Json::Value doTx (RPC::JsonContext& context)
{
if (!context.params.isMember (jss::transaction))
return rpcError (rpcINVALID_PARAMS);
@@ -198,4 +206,117 @@ Json::Value doTx (RPC::Context& context)
return ret;
}
} // ripple
std::pair<rpc::v1::GetTxResponse, grpc::Status>
doTxGrpc(RPC::GRPCContext<rpc::v1::GetTxRequest>& context)
{
// return values
rpc::v1::GetTxResponse result;
grpc::Status status = grpc::Status::OK;
// input
rpc::v1::GetTxRequest& request = context.params;
std::string const& hashBytes = request.hash();
uint256 hash = uint256::fromVoid(hashBytes.data());
// hash is included in the response
result.set_hash(request.hash());
auto ec{rpcSUCCESS};
// get the transaction
std::shared_ptr<Transaction> txn =
context.app.getMasterTransaction().fetch(hash, ec);
if (ec == rpcDB_DESERIALIZATION)
{
auto errorInfo = RPC::get_error_info(ec);
grpc::Status errorStatus{grpc::StatusCode::INTERNAL,
errorInfo.message.c_str()};
return {result, errorStatus};
}
if (!txn)
{
grpc::Status errorStatus{grpc::StatusCode::NOT_FOUND, "txn not found"};
return {result, errorStatus};
}
std::shared_ptr<STTx const> stTxn = txn->getSTransaction();
if (stTxn->getTxnType() != ttPAYMENT)
{
auto getTypeStr = [&stTxn]() {
return TxFormats::getInstance()
.findByType(stTxn->getTxnType())
->getName();
};
grpc::Status errorStatus{grpc::StatusCode::UNIMPLEMENTED,
"txn type not supported: " + getTypeStr()};
return {result, errorStatus};
}
// populate transaction data
if (request.binary())
{
Serializer s = stTxn->getSerializer();
result.set_transaction_binary(s.data(), s.size());
}
else
{
RPC::populateTransaction(*result.mutable_transaction(), stTxn);
}
result.set_ledger_index(txn->getLedger());
std::shared_ptr<Ledger const> ledger =
context.ledgerMaster.getLedgerBySeq(txn->getLedger());
// get meta data
if (ledger)
{
if (request.binary())
{
SHAMapTreeNode::TNType type;
auto const item = ledger->txMap().peekItem(txn->getID(), type);
if (item && type == SHAMapTreeNode::tnTRANSACTION_MD)
{
SerialIter it(item->slice());
it.skip(it.getVLDataLength()); // skip transaction
Blob blob = it.getVL();
Slice slice = makeSlice(blob);
result.set_meta_binary(slice.data(), slice.size());
bool validated = isValidated(
context.ledgerMaster,
ledger->info().seq,
ledger->info().hash);
result.set_validated(validated);
}
}
else
{
auto rawMeta = ledger->txRead(txn->getID()).second;
if (rawMeta)
{
auto txMeta = std::make_shared<TxMeta>(
txn->getID(), ledger->seq(), *rawMeta);
bool validated = isValidated(
context.ledgerMaster,
ledger->info().seq,
ledger->info().hash);
result.set_validated(validated);
RPC::populateMeta(*result.mutable_meta(), txMeta);
insertDeliveredAmount(
*result.mutable_meta()->mutable_delivered_amount(),
context,
txn,
*txMeta);
}
}
}
return {result, status};
}
} // namespace ripple

View File

@@ -34,7 +34,7 @@ namespace ripple {
// {
// start: <index>
// }
Json::Value doTxHistory (RPC::Context& context)
Json::Value doTxHistory (RPC::JsonContext& context)
{
context.loadType = Resource::feeMediumBurdenRPC;

View File

@@ -24,7 +24,7 @@
namespace ripple {
Json::Value doUnlList (RPC::Context& context)
Json::Value doUnlList (RPC::JsonContext& context)
{
Json::Value obj (Json::objectValue);

View File

@@ -28,7 +28,7 @@
namespace ripple {
Json::Value doUnsubscribe (RPC::Context& context)
Json::Value doUnsubscribe (RPC::JsonContext& context)
{
InfoSub::pointer ispSub;

View File

@@ -42,7 +42,7 @@ validationSeed (Json::Value const& params)
//
// This command requires Role::ADMIN access because it makes
// no sense to ask an untrusted server for this.
Json::Value doValidationCreate (RPC::Context& context)
Json::Value doValidationCreate (RPC::JsonContext& context)
{
Json::Value obj (Json::objectValue);

View File

@@ -24,7 +24,7 @@
namespace ripple {
Json::Value
doValidatorListSites(RPC::Context& context)
doValidatorListSites(RPC::JsonContext& context)
{
return context.app.validatorSites().getJson();
}

View File

@@ -24,7 +24,7 @@
namespace ripple {
Json::Value
doValidators(RPC::Context& context)
doValidators(RPC::JsonContext& context)
{
return context.app.validators().getJson();
}

View File

@@ -28,7 +28,7 @@ namespace RPC {
class VersionHandler
{
public:
explicit VersionHandler (Context&) {}
explicit VersionHandler (JsonContext&) {}
Status check()
{

View File

@@ -64,7 +64,7 @@ estimate_entropy (std::string const& input)
// {
// passphrase: <string>
// }
Json::Value doWalletPropose (RPC::Context& context)
Json::Value doWalletPropose (RPC::JsonContext& context)
{
return walletPropose (context.params);
}

View File

@@ -137,7 +137,7 @@ insertDeliveredAmount(
void
insertDeliveredAmount(
Json::Value& meta,
RPC::Context& context,
RPC::JsonContext& context,
std::shared_ptr<Transaction> transaction,
TxMeta const& transactionMeta)
{
@@ -172,5 +172,80 @@ insertDeliveredAmount(
std::move(serializedTx),
transactionMeta);
}
} // RPC
} // ripple
// TODO get rid of the code duplication between this function and the preceding
// function
void
insertDeliveredAmount(
rpc::v1::CurrencyAmount& proto,
RPC::Context& context,
std::shared_ptr<Transaction> transaction,
TxMeta const& transactionMeta)
{
if (!transaction)
return;
auto const serializedTx = transaction->getSTransaction();
if (!serializedTx)
return;
// These lambdas are used to compute the values lazily
auto const getFix1623Enabled = [&context]() -> bool {
auto const view = context.app.openLedger().current();
if (!view)
return false;
return view->rules().enabled(fix1623);
};
auto const getLedgerIndex = [&transaction]() -> LedgerIndex {
return transaction->getLedger();
};
auto const getCloseTime =
[&context, &transaction]() -> boost::optional<NetClock::time_point> {
return context.ledgerMaster.getCloseTimeBySeq(transaction->getLedger());
};
{
TxType const tt{serializedTx->getTxnType()};
if (tt != ttPAYMENT &&
tt != ttCHECK_CASH &&
tt != ttACCOUNT_DELETE)
return;
if (tt == ttCHECK_CASH &&
!getFix1623Enabled())
return;
}
// if the transaction failed nothing could have been delivered.
if (transactionMeta.getResultTER() != tesSUCCESS)
return;
if (transactionMeta.hasDeliveredAmount())
{
populateAmount(proto, transactionMeta.getDeliveredAmount());
return;
}
if (serializedTx->isFieldPresent(sfAmount))
{
using namespace std::chrono_literals;
// Ledger 4594095 is the first ledger in which the DeliveredAmount field
// was present when a partial payment was made and its absence indicates
// that the amount delivered is listed in the Amount field.
//
// If the ledger closed long after the DeliveredAmount code was deployed
// then its absence indicates that the amount delivered is listed in the
// Amount field. DeliveredAmount went live January 24, 2014.
// 446000000 is in Feb 2014, well after DeliveredAmount went live
if (getLedgerIndex() >= 4594095 ||
getCloseTime() > NetClock::time_point{446000000s})
{
populateAmount(proto, serializedTx->getFieldAmount(sfAmount));
return;
}
}
}
} // namespace RPC
} // namespace ripple

View File

@@ -30,7 +30,7 @@ namespace {
template <typename Function>
Handler::Method<Json::Value> byRef (Function const& f)
{
return [f] (Context& context, Json::Value& result)
return [f] (JsonContext& context, Json::Value& result)
{
result = f (context);
if (result.type() != Json::objectValue)
@@ -44,7 +44,7 @@ Handler::Method<Json::Value> byRef (Function const& f)
}
template <class Object, class HandlerImpl>
Status handle (Context& context, Object& object)
Status handle (JsonContext& context, Object& object)
{
HandlerImpl handler (context);

View File

@@ -23,6 +23,9 @@
#include <ripple/core/Config.h>
#include <ripple/rpc/RPCHandler.h>
#include <ripple/rpc/Status.h>
#include <ripple/rpc/impl/Tuning.h>
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <vector>
namespace Json {
@@ -43,7 +46,7 @@ enum Condition {
struct Handler
{
template <class JsonValue>
using Method = std::function <Status (Context&, JsonValue&)>;
using Method = std::function <Status (JsonContext&, JsonValue&)>;
const char* name_;
Method<Json::Value> valueMethod_;
@@ -66,6 +69,55 @@ Json::Value makeObjectValue (
/** Return names of all methods. */
std::vector<char const*> getHandlerNames();
template <class T>
error_code_i conditionMet(Condition condition_required, T& context)
{
if ((condition_required & NEEDS_NETWORK_CONNECTION) &&
(context.netOps.getOperatingMode () < OperatingMode::SYNCING))
{
JLOG (context.j.info())
<< "Insufficient network mode for RPC: "
<< context.netOps.strOperatingMode ();
return rpcNO_NETWORK;
}
if (context.app.getOPs().isAmendmentBlocked() &&
(condition_required & NEEDS_CURRENT_LEDGER ||
condition_required & NEEDS_CLOSED_LEDGER))
{
return rpcAMENDMENT_BLOCKED;
}
if (!context.app.config().standalone() &&
condition_required & NEEDS_CURRENT_LEDGER)
{
if (context.ledgerMaster.getValidatedLedgerAge () >
Tuning::maxValidatedLedgerAge)
{
return rpcNO_CURRENT;
}
auto const cID = context.ledgerMaster.getCurrentLedgerIndex ();
auto const vID = context.ledgerMaster.getValidLedgerIndex ();
if (cID + 10 < vID)
{
JLOG (context.j.debug()) << "Current ledger ID(" << cID <<
") is less than validated ledger ID(" << vID << ")";
return rpcNO_CURRENT;
}
}
if ((condition_required & NEEDS_CLOSED_LEDGER) &&
!context.ledgerMaster.getClosedLedger ())
{
return rpcNO_CLOSED;
}
return rpcSUCCESS;
}
} // RPC
} // ripple

View File

@@ -111,7 +111,7 @@ namespace {
*/
error_code_i fillHandler (Context& context,
error_code_i fillHandler (JsonContext& context,
Handler const * & result)
{
if (! isUnlimited (context.role))
@@ -149,47 +149,10 @@ error_code_i fillHandler (Context& context,
if (handler->role_ == Role::ADMIN && context.role != Role::ADMIN)
return rpcNO_PERMISSION;
if ((handler->condition_ & NEEDS_NETWORK_CONNECTION) &&
(context.netOps.getOperatingMode () < OperatingMode::SYNCING))
error_code_i res = conditionMet(handler->condition_,context);
if(res != rpcSUCCESS)
{
JLOG (context.j.info())
<< "Insufficient network mode for RPC: "
<< context.netOps.strOperatingMode ();
return rpcNO_NETWORK;
}
if (context.app.getOPs().isAmendmentBlocked() &&
(handler->condition_ & NEEDS_CURRENT_LEDGER ||
handler->condition_ & NEEDS_CLOSED_LEDGER))
{
return rpcAMENDMENT_BLOCKED;
}
if (!context.app.config().standalone() &&
handler->condition_ & NEEDS_CURRENT_LEDGER)
{
if (context.ledgerMaster.getValidatedLedgerAge () >
Tuning::maxValidatedLedgerAge)
{
return rpcNO_CURRENT;
}
auto const cID = context.ledgerMaster.getCurrentLedgerIndex ();
auto const vID = context.ledgerMaster.getValidLedgerIndex ();
if (cID + 10 < vID)
{
JLOG (context.j.debug()) << "Current ledger ID(" << cID <<
") is less than validated ledger ID(" << vID << ")";
return rpcNO_CURRENT;
}
}
if ((handler->condition_ & NEEDS_CLOSED_LEDGER) &&
!context.ledgerMaster.getClosedLedger ())
{
return rpcNO_CLOSED;
return res;
}
result = handler;
@@ -198,7 +161,7 @@ error_code_i fillHandler (Context& context,
template <class Object, class Method>
Status callMethod (
Context& context, Method method, std::string const& name, Object& result)
JsonContext& context, Method method, std::string const& name, Object& result)
{
static std::atomic<std::uint64_t> requestId {0};
auto& perfLog = context.app.getPerfLog();
@@ -228,7 +191,7 @@ Status callMethod (
template <class Method, class Object>
void getResult (
Context& context, Method method, Object& object, std::string const& name)
JsonContext& context, Method method, Object& object, std::string const& name)
{
auto&& result = Json::addObject (object, jss::result);
if (auto status = callMethod (context, method, name, result))
@@ -261,7 +224,7 @@ void getResult (
} // namespace
Status doCommand (
RPC::Context& context, Json::Value& result)
RPC::JsonContext& context, Json::Value& result)
{
Handler const * handler = nullptr;
if (auto error = fillHandler (context, handler))

View File

@@ -49,20 +49,20 @@ accountFromStringStrict(std::string const& account)
return result;
}
Json::Value
accountFromString(
error_code_i
accountFromStringWithCode(
AccountID& result, std::string const& strIdent, bool bStrict)
{
if (auto accountID = accountFromStringStrict (strIdent))
{
result = *accountID;
return Json::objectValue;
return rpcSUCCESS;
}
if (bStrict)
{
auto id = deprecatedParseBitcoinAccountID (strIdent);
return rpcError (id ? rpcACT_BITCOIN : rpcACT_MALFORMED);
return id ? rpcACT_BITCOIN : rpcACT_MALFORMED;
}
// We allow the use of the seeds which is poor practice
@@ -70,14 +70,26 @@ accountFromString(
auto const seed = parseGenericSeed (strIdent);
if (!seed)
return rpcError (rpcBAD_SEED);
return rpcBAD_SEED;
auto const keypair = generateKeyPair (
KeyType::secp256k1,
*seed);
result = calcAccountID (keypair.first);
return Json::objectValue;
return rpcSUCCESS;
}
Json::Value
accountFromString(
AccountID& result, std::string const& strIdent, bool bStrict)
{
error_code_i code = accountFromStringWithCode(result, strIdent, bStrict);
if(code != rpcSUCCESS)
return rpcError(code);
else
return Json::objectValue;
}
bool
@@ -185,7 +197,7 @@ isValidatedOld(LedgerMaster& ledgerMaster, bool standalone)
template <class T>
Status
ledgerFromRequest(T& ledger, Context& context)
ledgerFromRequest(T& ledger, JsonContext& context)
{
static auto const minSequenceGap = 10;
@@ -286,6 +298,106 @@ ledgerFromRequest(T& ledger, Context& context)
return Status::OK;
}
} // namespace
template <class T>
Status
ledgerFromRequest(
T& ledger,
GRPCContext<rpc::v1::GetAccountInfoRequest>& context)
{
static auto const minSequenceGap = 10;
ledger.reset();
rpc::v1::GetAccountInfoRequest& request = context.params;
auto& ledgerMaster = context.ledgerMaster;
using LedgerCase = rpc::v1::LedgerSpecifier::LedgerCase;
LedgerCase ledgerCase = request.ledger().ledger_case();
if (ledgerCase == LedgerCase::kHash)
{
uint256 ledgerHash = uint256::fromVoid(request.ledger().hash().data());
if (ledgerHash.size() != request.ledger().hash().size())
return {rpcINVALID_PARAMS, "ledgerHashMalformed"};
ledger = ledgerMaster.getLedgerByHash(ledgerHash);
if (ledger == nullptr)
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
}
else if (ledgerCase == LedgerCase::kSequence)
{
ledger = ledgerMaster.getLedgerBySeq(request.ledger().sequence());
if (ledger == nullptr)
{
auto cur = ledgerMaster.getCurrentLedger();
if (cur->info().seq == request.ledger().sequence())
ledger = cur;
}
if (ledger == nullptr)
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
if (ledger->info().seq > ledgerMaster.getValidLedgerIndex() &&
isValidatedOld(ledgerMaster, context.app.config().standalone()))
{
ledger.reset();
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
}
}
else if (
ledgerCase == LedgerCase::kShortcut ||
ledgerCase == LedgerCase::LEDGER_NOT_SET)
{
if (isValidatedOld(ledgerMaster, context.app.config().standalone()))
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
auto const shortcut = request.ledger().shortcut();
if (shortcut == rpc::v1::LedgerSpecifier::SHORTCUT_VALIDATED)
{
ledger = ledgerMaster.getValidatedLedger();
if (ledger == nullptr)
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
assert(!ledger->open());
}
else
{
// note, if unspecified, defaults to current ledger
if (shortcut == rpc::v1::LedgerSpecifier::SHORTCUT_UNSPECIFIED ||
shortcut == rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT)
{
ledger = ledgerMaster.getCurrentLedger();
assert(ledger->open());
}
else if (shortcut == rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED)
{
ledger = ledgerMaster.getClosedLedger();
assert(!ledger->open());
}
if (ledger == nullptr)
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
if (ledger->info().seq + minSequenceGap <
ledgerMaster.getValidLedgerIndex())
{
ledger.reset();
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
}
}
}
return Status::OK;
}
// explicit instantiation of above function
template Status
ledgerFromRequest<>(
std::shared_ptr<ReadView const>&,
GRPCContext<rpc::v1::GetAccountInfoRequest>&);
bool
isValidated(LedgerMaster& ledgerMaster, ReadView const& ledger,
@@ -334,7 +446,6 @@ isValidated(LedgerMaster& ledgerMaster, ReadView const& ledger,
return true;
}
} // namespace
// The previous version of the lookupLedger command would accept the
// "ledger_index" argument as a string and silently treat it as a request to
@@ -356,7 +467,7 @@ isValidated(LedgerMaster& ledgerMaster, ReadView const& ledger,
// optionally the fields "ledger_hash", "ledger_index" and
// "ledger_current_index", if they are defined.
Status
lookupLedger(std::shared_ptr<ReadView const>& ledger, Context& context,
lookupLedger(std::shared_ptr<ReadView const>& ledger, JsonContext& context,
Json::Value& result)
{
if (auto status = ledgerFromRequest (ledger, context))
@@ -379,7 +490,7 @@ lookupLedger(std::shared_ptr<ReadView const>& ledger, Context& context,
}
Json::Value
lookupLedger(std::shared_ptr<ReadView const>& ledger, Context& context)
lookupLedger(std::shared_ptr<ReadView const>& ledger, JsonContext& context)
{
Json::Value result;
if (auto status = lookupLedger (ledger, context, result))
@@ -433,7 +544,7 @@ injectSLE(Json::Value& jv, SLE const& sle)
boost::optional<Json::Value>
readLimitField(unsigned int& limit, Tuning::LimitRange const& range,
Context const& context)
JsonContext const& context)
{
limit = range.rdefault;
if (auto const& jvLimit = context.params[jss::limit])
@@ -712,6 +823,672 @@ chooseLedgerEntryType(Json::Value const& params)
return result;
}
void
populateAccountRoot(rpc::v1::AccountRoot& proto, STObject const& obj)
{
if (obj.isFieldPresent(sfAccount))
{
AccountID account = obj.getAccountID(sfAccount);
proto.mutable_account()->set_address(toBase58(account));
}
if (obj.isFieldPresent(sfBalance))
{
STAmount amount = obj.getFieldAmount(sfBalance);
proto.mutable_balance()->set_drops(amount.xrp().drops());
}
if (obj.isFieldPresent(sfSequence))
{
proto.set_sequence(obj.getFieldU32(sfSequence));
}
if (obj.isFieldPresent(sfFlags))
{
proto.set_flags(obj.getFieldU32(sfFlags));
}
if (obj.isFieldPresent(sfOwnerCount))
{
proto.set_owner_count(obj.getFieldU32(sfOwnerCount));
}
if (obj.isFieldPresent(sfPreviousTxnID))
{
auto field = obj.getFieldH256(sfPreviousTxnID);
proto.set_previous_transaction_id(field.data(), field.size());
}
if (obj.isFieldPresent(sfPreviousTxnLgrSeq))
{
proto.set_previous_transaction_ledger_sequence(
obj.getFieldU32(sfPreviousTxnLgrSeq));
}
if (obj.isFieldPresent(sfAccountTxnID))
{
auto field = obj.getFieldH256(sfAccountTxnID);
proto.set_account_transaction_id(field.data(), field.size());
}
if (obj.isFieldPresent(sfDomain))
{
auto field = obj.getFieldH256(sfDomain);
proto.set_domain(field.data(), field.size());
}
if (obj.isFieldPresent(sfEmailHash))
{
auto field = obj.getFieldH128(sfEmailHash);
proto.set_email_hash(field.data(), field.size());
}
if (obj.isFieldPresent(sfMessageKey))
{
auto field = obj.getFieldVL(sfMessageKey);
proto.set_message_key(field.data(), field.size());
}
if (obj.isFieldPresent(sfRegularKey))
{
proto.set_regular_key(toBase58(obj.getAccountID(sfRegularKey)));
}
if (obj.isFieldPresent(sfTickSize))
{
proto.set_tick_size(obj.getFieldU8(sfTickSize));
}
if (obj.isFieldPresent(sfTransferRate))
{
proto.set_transfer_rate(obj.getFieldU32(sfTransferRate));
}
}
void
populateRippleState(rpc::v1::RippleState& proto, STObject const& obj)
{
if (obj.isFieldPresent(sfBalance))
{
STAmount amount = obj.getFieldAmount(sfBalance);
populateAmount(*proto.mutable_balance(), amount);
}
if (obj.isFieldPresent(sfFlags))
{
proto.set_flags(obj.getFieldU32(sfFlags));
}
if (obj.isFieldPresent(sfLowLimit))
{
STAmount amount = obj.getFieldAmount(sfLowLimit);
populateAmount(*proto.mutable_low_limit(), amount);
}
if (obj.isFieldPresent(sfHighLimit))
{
STAmount amount = obj.getFieldAmount(sfHighLimit);
populateAmount(*proto.mutable_high_limit(), amount);
}
if (obj.isFieldPresent(sfLowNode))
{
proto.set_low_node(obj.getFieldU64(sfLowNode));
}
if (obj.isFieldPresent(sfHighNode))
{
proto.set_high_node(obj.getFieldU64(sfHighNode));
}
if (obj.isFieldPresent(sfLowQualityIn))
{
proto.set_low_quality_in(obj.getFieldU32(sfLowQualityIn));
}
if (obj.isFieldPresent(sfLowQualityOut))
{
proto.set_low_quality_out(obj.getFieldU32(sfLowQualityOut));
}
if (obj.isFieldPresent(sfHighQualityIn))
{
proto.set_high_quality_in(obj.getFieldU32(sfHighQualityIn));
}
if (obj.isFieldPresent(sfHighQualityOut))
{
proto.set_high_quality_out(obj.getFieldU32(sfHighQualityOut));
}
}
void
populateOffer(rpc::v1::Offer& proto, STObject const& obj)
{
if (obj.isFieldPresent(sfAccount))
{
AccountID account = obj.getAccountID(sfAccount);
proto.set_account(toBase58(account));
}
if (obj.isFieldPresent(sfSequence))
{
proto.set_sequence(obj.getFieldU32(sfSequence));
}
if (obj.isFieldPresent(sfFlags))
{
proto.set_flags(obj.getFieldU32(sfFlags));
}
if (obj.isFieldPresent(sfTakerPays))
{
STAmount amount = obj.getFieldAmount(sfTakerPays);
populateAmount(*proto.mutable_taker_pays(), amount);
}
if (obj.isFieldPresent(sfTakerGets))
{
STAmount amount = obj.getFieldAmount(sfTakerGets);
populateAmount(*proto.mutable_taker_gets(), amount);
}
if (obj.isFieldPresent(sfBookDirectory))
{
auto field = obj.getFieldVL(sfBookDirectory);
proto.set_book_directory(field.data(), field.size());
}
if (obj.isFieldPresent(sfBookNode))
{
proto.set_book_node(obj.getFieldU64(sfBookNode));
}
if (obj.isFieldPresent(sfExpiration))
{
proto.set_expiration(obj.getFieldU32(sfExpiration));
}
}
void
populateSignerList(rpc::v1::SignerList& proto, STObject const& obj)
{
proto.set_flags(obj.getFieldU32(sfFlags));
auto prevTxnID = obj.getFieldH256(sfPreviousTxnID);
proto.set_previous_txn_id(prevTxnID.data(), prevTxnID.size());
proto.set_previous_transaction_ledger_sequence(
obj.getFieldU32(sfPreviousTxnLgrSeq));
proto.set_owner_node(obj.getFieldU64(sfOwnerNode));
proto.set_signer_list_id(obj.getFieldU32(sfSignerListID));
proto.set_signer_quorum(obj.getFieldU32(sfSignerQuorum));
STArray const& signerEntries = obj.getFieldArray(sfSignerEntries);
for (auto it = signerEntries.begin(); it != signerEntries.end(); ++it)
{
rpc::v1::SignerEntry& signerEntryProto = *proto.add_signer_entries();
signerEntryProto.mutable_account()->set_address(
toBase58(it->getAccountID(sfAccount)));
signerEntryProto.set_signer_weight(it->getFieldU16(sfSignerWeight));
}
}
void
populateQueueData(
rpc::v1::QueueData& proto,
std::map<TxSeq, TxQ::AccountTxDetails const> const& txs)
{
if (!txs.empty())
{
proto.set_txn_count(txs.size());
proto.set_lowest_sequence(txs.begin()->first);
proto.set_highest_sequence(txs.rbegin()->first);
boost::optional<bool> anyAuthChanged(false);
boost::optional<XRPAmount> totalSpend(0);
for (auto const& [txSeq, txDetails] : txs)
{
rpc::v1::QueuedTransaction& qt = *proto.add_transactions();
qt.set_sequence(txSeq);
qt.set_fee_level(txDetails.feeLevel.fee());
if (txDetails.lastValid)
qt.set_last_ledger_sequence(*txDetails.lastValid);
if (txDetails.consequences)
{
qt.mutable_fee()->set_drops(
txDetails.consequences->fee.drops());
auto spend = txDetails.consequences->potentialSpend +
txDetails.consequences->fee;
qt.mutable_max_spend_drops()->set_drops(spend.drops());
if (totalSpend)
*totalSpend += spend;
auto authChanged =
txDetails.consequences->category == TxConsequences::blocker;
if (authChanged)
anyAuthChanged.emplace(authChanged);
qt.set_auth_change(authChanged);
}
else
{
if (anyAuthChanged && !*anyAuthChanged)
anyAuthChanged.reset();
totalSpend.reset();
}
}
if (anyAuthChanged)
proto.set_auth_change_queued(*anyAuthChanged);
if (totalSpend)
proto.mutable_max_spend_drops_total()->set_drops(
(*totalSpend).drops());
}
}
void
populateDirectoryNode(rpc::v1::DirectoryNode& proto, STObject const& obj)
{
if (obj.isFieldPresent(sfOwner))
{
AccountID ownerAccount = obj.getAccountID(sfAccount);
proto.set_owner(toBase58(ownerAccount));
}
if (obj.isFieldPresent(sfTakerPaysCurrency))
{
uint160 tpCurr = obj.getFieldH160(sfTakerPaysCurrency);
proto.mutable_taker_pays_currency()->set_code(
tpCurr.data(), tpCurr.size());
}
if (obj.isFieldPresent(sfTakerPaysIssuer))
{
uint160 tpIss = obj.getFieldH160(sfTakerPaysIssuer);
proto.set_taker_pays_issuer(tpIss.data(), tpIss.size());
}
if (obj.isFieldPresent(sfTakerGetsCurrency))
{
uint160 tgCurr = obj.getFieldH160(sfTakerGetsCurrency);
proto.mutable_taker_gets_currency()->set_code(
tgCurr.data(), tgCurr.size());
}
if (obj.isFieldPresent(sfTakerGetsIssuer))
{
uint160 tgIss = obj.getFieldH160(sfTakerGetsIssuer);
proto.set_taker_gets_issuer(tgIss.data(), tgIss.size());
}
if (obj.isFieldPresent(sfIndexes))
{
const STVector256& vec = obj.getFieldV256(sfIndexes);
for (size_t i = 0; i < vec.size(); ++i)
{
uint256 const& elt = vec[i];
proto.add_indexes(elt.data(), elt.size());
}
}
if (obj.isFieldPresent(sfRootIndex))
{
uint256 rootIndex = obj.getFieldH256(sfRootIndex);
proto.set_root_index(rootIndex.data(), rootIndex.size());
}
if (obj.isFieldPresent(sfIndexNext))
{
proto.set_index_next(obj.getFieldU64(sfIndexNext));
}
if (obj.isFieldPresent(sfIndexPrevious))
{
proto.set_index_previous(obj.getFieldU64(sfIndexPrevious));
}
}
void
populateLedgerEntryType(rpc::v1::AffectedNode& proto, std::uint16_t lgrType)
{
switch (lgrType)
{
case ltACCOUNT_ROOT:
proto.set_ledger_entry_type(
rpc::v1::LEDGER_ENTRY_TYPE_ACCOUNT_ROOT);
break;
case ltDIR_NODE:
proto.set_ledger_entry_type(
rpc::v1::LEDGER_ENTRY_TYPE_DIRECTORY_NODE);
break;
case ltRIPPLE_STATE:
proto.set_ledger_entry_type(
rpc::v1::LEDGER_ENTRY_TYPE_RIPPLE_STATE);
break;
case ltSIGNER_LIST:
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_SIGNER_LIST);
break;
case ltOFFER:
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_OFFER);
break;
case ltLEDGER_HASHES:
proto.set_ledger_entry_type(
rpc::v1::LEDGER_ENTRY_TYPE_LEDGER_HASHES);
break;
case ltAMENDMENTS:
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_AMENDMENTS);
break;
case ltFEE_SETTINGS:
proto.set_ledger_entry_type(
rpc::v1::LEDGER_ENTRY_TYPE_FEE_SETTINGS);
break;
case ltESCROW:
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_ESCROW);
break;
case ltPAYCHAN:
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_PAY_CHANNEL);
break;
case ltCHECK:
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_CHECK);
break;
case ltDEPOSIT_PREAUTH:
proto.set_ledger_entry_type(
rpc::v1::LEDGER_ENTRY_TYPE_DEPOSIT_PREAUTH);
break;
}
}
template <class T>
void
populateFields(T& proto, STObject const& obj, std::uint16_t type)
{
if (type == ltACCOUNT_ROOT)
{
RPC::populateAccountRoot(*proto.mutable_account_root(), obj);
}
else if (type == ltRIPPLE_STATE)
{
RPC::populateRippleState(*proto.mutable_ripple_state(), obj);
}
else if (type == ltOFFER)
{
RPC::populateOffer(*proto.mutable_offer(), obj);
}
else if (type == ltDIR_NODE)
{
RPC::populateDirectoryNode(*proto.mutable_directory_node(), obj);
}
else
{
// Ledger object not supported by protobuf/grpc yet
}
}
void
populateMeta(rpc::v1::Meta& proto, std::shared_ptr<TxMeta> txMeta)
{
proto.set_transaction_index(txMeta->getIndex());
populateTransactionResultType(
*proto.mutable_transaction_result(), txMeta->getResultTER());
proto.mutable_transaction_result()->set_result(
transToken(txMeta->getResultTER()));
STArray& nodes = txMeta->getNodes();
for (auto it = nodes.begin(); it != nodes.end(); ++it)
{
STObject& obj = *it;
rpc::v1::AffectedNode* node = proto.add_affected_nodes();
// ledger index
uint256 ledgerIndex = obj.getFieldH256(sfLedgerIndex);
node->set_ledger_index(ledgerIndex.data(), ledgerIndex.size());
// ledger entry type
std::uint16_t lgrType = obj.getFieldU16(sfLedgerEntryType);
populateLedgerEntryType(*node, lgrType);
// modified node
if (obj.getFName() == sfModifiedNode)
{
// final fields
if (obj.isFieldPresent(sfFinalFields))
{
STObject& finalFields =
obj.getField(sfFinalFields).downcast<STObject>();
rpc::v1::LedgerObject* finalFieldsProto =
node->mutable_modified_node()->mutable_final_fields();
populateFields(*finalFieldsProto, finalFields, lgrType);
}
// previous fields
if (obj.isFieldPresent(sfPreviousFields))
{
STObject& prevFields =
obj.getField(sfPreviousFields).downcast<STObject>();
rpc::v1::LedgerObject* prevFieldsProto =
node->mutable_modified_node()->mutable_previous_fields();
populateFields(*prevFieldsProto, prevFields, lgrType);
}
// prev txn id and prev txn ledger seq
uint256 prevTxnId = obj.getFieldH256(sfPreviousTxnID);
node->mutable_modified_node()->set_previous_transaction_id(
prevTxnId.data(), prevTxnId.size());
node->mutable_modified_node()
->set_previous_transaction_ledger_sequence(
obj.getFieldU32(sfPreviousTxnLgrSeq));
}
// created node
else if (obj.getFName() == sfCreatedNode)
{
// new fields
if (obj.isFieldPresent(sfNewFields))
{
STObject& newFields =
obj.getField(sfNewFields).downcast<STObject>();
rpc::v1::LedgerObject* newFieldsProto =
node->mutable_created_node()->mutable_new_fields();
populateFields(*newFieldsProto, newFields, lgrType);
}
}
// deleted node
else if (obj.getFName() == sfDeletedNode)
{
// final fields
if (obj.isFieldPresent(sfFinalFields))
{
STObject& finalFields =
obj.getField(sfFinalFields).downcast<STObject>();
rpc::v1::LedgerObject* finalFieldsProto =
node->mutable_deleted_node()->mutable_final_fields();
populateFields(*finalFieldsProto, finalFields, lgrType);
}
}
}
}
void
populateAmount(rpc::v1::CurrencyAmount& proto, STAmount const& amount)
{
if (amount.native())
{
proto.mutable_xrp_amount()->set_drops(amount.xrp().drops());
}
else
{
rpc::v1::IssuedCurrencyAmount* issued =
proto.mutable_issued_currency_amount();
Issue const& issue = amount.issue();
Currency currency = issue.currency;
issued->mutable_currency()->set_name(to_string(issue.currency));
issued->mutable_currency()->set_code(currency.data(), currency.size());
issued->set_value(to_string(amount.iou()));
issued->mutable_issuer()->set_address(toBase58(issue.account));
}
}
void
populateTransaction(
rpc::v1::Transaction& proto,
std::shared_ptr<STTx const> txnSt)
{
AccountID account = txnSt->getAccountID(sfAccount);
proto.mutable_account()->set_address(toBase58(account));
STAmount amount = txnSt->getFieldAmount(sfAmount);
populateAmount(*proto.mutable_payment()->mutable_amount(), amount);
AccountID accountDest = txnSt->getAccountID(sfDestination);
proto.mutable_payment()->mutable_destination()->set_address(
toBase58(accountDest));
STAmount fee = txnSt->getFieldAmount(sfFee);
proto.mutable_fee()->set_drops(fee.xrp().drops());
proto.set_sequence(txnSt->getFieldU32(sfSequence));
Blob signingPubKey = txnSt->getFieldVL(sfSigningPubKey);
proto.set_signing_public_key(signingPubKey.data(), signingPubKey.size());
proto.set_flags(txnSt->getFieldU32(sfFlags));
proto.set_last_ledger_sequence(txnSt->getFieldU32(sfLastLedgerSequence));
Blob blob = txnSt->getFieldVL(sfTxnSignature);
proto.set_signature(blob.data(), blob.size());
if (txnSt->isFieldPresent(sfSourceTag))
{
proto.set_source_tag(txnSt->getFieldU32(sfSourceTag));
}
if (txnSt->isFieldPresent(sfAccountTxnID))
{
auto field = txnSt->getFieldH256(sfAccountTxnID);
proto.set_account_transaction_id(field.data(), field.size());
}
if (txnSt->isFieldPresent(sfMemos))
{
auto memos = txnSt->getFieldArray(sfMemos);
for (auto it = memos.begin(); it != memos.end(); ++it)
{
rpc::v1::Memo* elt = proto.add_memos();
auto memo = it->getField(sfMemo).downcast<STObject>();
if (memo.isFieldPresent(sfMemoData))
{
auto memoData = memo.getFieldVL(sfMemoData);
elt->set_memo_data(memoData.data(), memoData.size());
}
if (memo.isFieldPresent(sfMemoFormat))
{
auto memoFormat = memo.getFieldVL(sfMemoFormat);
elt->set_memo_format(memoFormat.data(), memoFormat.size());
}
if (memo.isFieldPresent(sfMemoType))
{
auto memoType = memo.getFieldVL(sfMemoType);
elt->set_memo_type(memoType.data(), memoType.size());
}
}
}
if (txnSt->isFieldPresent(sfSigners))
{
auto signers = txnSt->getFieldArray(sfSigners);
for (auto it = signers.begin(); it != signers.end(); ++it)
{
rpc::v1::Signer* elt = proto.add_signers();
auto signer = it->getField(sfSigner).downcast<STObject>();
if (signer.isFieldPresent(sfAccount))
{
elt->mutable_account()->set_address(
toBase58(signer.getAccountID(sfAccount)));
}
if (signer.isFieldPresent(sfTxnSignature))
{
auto sig = signer.getFieldVL(sfTxnSignature);
elt->set_transaction_signature(sig.data(), sig.size());
}
if (signer.isFieldPresent(sfSigningPubKey))
{
auto pubKey = signer.getFieldVL(sfSigningPubKey);
elt->set_signing_public_key(pubKey.data(), pubKey.size());
}
}
}
if (safe_cast<TxType>(txnSt->getFieldU16(sfTransactionType)) ==
TxType::ttPAYMENT)
{
if (txnSt->isFieldPresent(sfSendMax))
{
STAmount const& sendMax = txnSt->getFieldAmount(sfSendMax);
populateAmount(
*proto.mutable_payment()->mutable_send_max(), sendMax);
}
if (txnSt->isFieldPresent(sfInvoiceID))
{
auto invoice = txnSt->getFieldH256(sfInvoiceID);
proto.mutable_payment()->set_invoice_id(
invoice.data(), invoice.size());
}
if (txnSt->isFieldPresent(sfDestinationTag))
{
proto.mutable_payment()->set_destination_tag(
txnSt->getFieldU32(sfDestinationTag));
}
// populate path data
STPathSet const& pathset = txnSt->getFieldPathSet(sfPaths);
for (auto it = pathset.begin(); it < pathset.end(); ++it)
{
STPath const& path = *it;
rpc::v1::Path* protoPath = proto.mutable_payment()->add_paths();
for (auto it2 = path.begin(); it2 != path.end(); ++it2)
{
rpc::v1::PathElement* protoElement = protoPath->add_elements();
STPathElement const& elt = *it2;
if (elt.isOffer())
{
if (elt.hasCurrency())
{
Currency const& currency = elt.getCurrency();
protoElement->mutable_currency()->set_name(
to_string(currency));
}
if (elt.hasIssuer())
{
AccountID const& issuer = elt.getIssuerID();
protoElement->mutable_issuer()->set_address(
toBase58(issuer));
}
}
else
{
AccountID const& pathAccount = elt.getAccountID();
protoElement->mutable_account()->set_address(
toBase58(pathAccount));
}
}
}
}
}
void
populateTransactionResultType(rpc::v1::TransactionResult& proto, TER result)
{
if (isTecClaim(result))
{
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TEC);
}
if (isTefFailure(result))
{
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TEF);
}
if (isTelLocal(result))
{
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TEL);
}
if (isTemMalformed(result))
{
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TEM);
}
if (isTerRetry(result))
{
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TER);
}
if (isTesSuccess(result))
{
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TES);
}
}
beast::SemanticVersion const firstVersion("1.0.0");
beast::SemanticVersion const goodVersion("1.0.0");
beast::SemanticVersion const lastVersion("1.0.0");

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