Merge pull request #86 from moneroexamples/devel

Add support for Monero 0.12.0, sub-addresses, JS decoding/proving txs and stagenet
master
moneroexamples 7 years ago committed by GitHub
commit 53e50edc61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

1
.gitignore vendored

@ -6,4 +6,5 @@
*.log *.log
*.orig *.orig
tests/ tests/
build/
cmake-build-debug/ cmake-build-debug/

@ -9,6 +9,7 @@ project(${PROJECT_NAME})
set(CMAKE_CXX_FLAGS set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -std=c++14") "${CMAKE_CXX_FLAGS} -std=c++14")
if (WIN32) if (WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj -O3") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj -O3")
endif() endif()
@ -52,7 +53,8 @@ find_package(Boost COMPONENTS
date_time date_time
REQUIRED) REQUIRED)
#info https://github.com/arsenm/sanitizers-cmake
find_package(Sanitizers)
if(APPLE) if(APPLE)
include_directories(/usr/local/opt/openssl/include) include_directories(/usr/local/opt/openssl/include)
@ -68,6 +70,7 @@ endif()
# include boost headers # include boost headers
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
include_directories("ext/mstch/include") include_directories("ext/mstch/include")
include_directories("ext/crow")
# add ext/ subfolder # add ext/ subfolder
add_subdirectory(ext/) add_subdirectory(ext/)
@ -84,11 +87,14 @@ set(SOURCE_FILES
add_executable(${PROJECT_NAME} add_executable(${PROJECT_NAME}
${SOURCE_FILES}) ${SOURCE_FILES})
add_sanitizers(${PROJECT_NAME})
create_git_version() create_git_version()
configure_files(${CMAKE_CURRENT_SOURCE_DIR}/src/templates ${CMAKE_CURRENT_BINARY_DIR}/templates) configure_files(${CMAKE_CURRENT_SOURCE_DIR}/src/templates ${CMAKE_CURRENT_BINARY_DIR}/templates)
configure_files(${CMAKE_CURRENT_SOURCE_DIR}/src/templates/css ${CMAKE_CURRENT_BINARY_DIR}/templates/css) configure_files(${CMAKE_CURRENT_SOURCE_DIR}/src/templates/css ${CMAKE_CURRENT_BINARY_DIR}/templates/css)
configure_files(${CMAKE_CURRENT_SOURCE_DIR}/src/templates/partials ${CMAKE_CURRENT_BINARY_DIR}/templates/partials) configure_files(${CMAKE_CURRENT_SOURCE_DIR}/src/templates/partials ${CMAKE_CURRENT_BINARY_DIR}/templates/partials)
configure_files(${CMAKE_CURRENT_SOURCE_DIR}/src/templates/js ${CMAKE_CURRENT_BINARY_DIR}/templates/js)
set(LIBRARIES set(LIBRARIES
myxrm myxrm
@ -99,15 +105,20 @@ set(LIBRARIES
cryptonote_core cryptonote_core
cryptonote_protocol cryptonote_protocol
cryptonote_basic cryptonote_basic
multisig
daemonizer daemonizer
cncrypto cncrypto
blocks blocks
lmdb lmdb
ringct ringct
ringct_basic
device
common common
mnemonics mnemonics
epee
easylogging easylogging
checkpoints
version
epee
${Boost_LIBRARIES} ${Boost_LIBRARIES}
pthread pthread
unbound unbound
@ -116,7 +127,7 @@ set(LIBRARIES
ssl) ssl)
if(APPLE) if(APPLE)
set(LIBRARIES ${LIBRARIES} "-framework IOKit") set(LIBRARIES ${LIBRARIES} "-framework IOKit -framework PCSC")
else() else()
set(LIBRARIES ${LIBRARIES} atomic) set(LIBRARIES ${LIBRARIES} atomic)
endif() endif()

@ -8,7 +8,7 @@ special importance to privacy-oriented users:
- track users activates through google analytics, - track users activates through google analytics,
- are closed sourced, - are closed sourced,
- are not available as hidden services, - are not available as hidden services,
- do not support Monero testnet, - do not support Monero testnet nor stagenet networks,
- have limited JSON API. - have limited JSON API.
@ -29,7 +29,7 @@ Tor users:
Clearnet versions: Clearnet versions:
- [https://oculus.serveo.net/](https://oculus.serveo.net/) - bleeding edge version. - [https://oculus.serveo.net/](https://oculus.serveo.net/) - bleeding edge version (down for now).
- [https://xmrchain.net/](https://xmrchain.net/) - https enabled, most popular and very stable. - [https://xmrchain.net/](https://xmrchain.net/) - https enabled, most popular and very stable.
- [https://MoneroExplorer.com/](https://moneroexplorer.com/) - nice looking one, https enabled. - [https://MoneroExplorer.com/](https://moneroexplorer.com/) - nice looking one, https enabled.
- [https://monerohash.com/explorer/](https://monerohash.com/explorer/) - nice looking one, https enabled. - [https://monerohash.com/explorer/](https://monerohash.com/explorer/) - nice looking one, https enabled.
@ -37,15 +37,20 @@ Clearnet versions:
- [https://moneroexplorer.pro/](https://moneroexplorer.pro/) - nice looking one, https enabled. - [https://moneroexplorer.pro/](https://moneroexplorer.pro/) - nice looking one, https enabled.
- [https://explorer.monero-otc.com/](https://explorer.monero-otc.com/) - https enabled. - [https://explorer.monero-otc.com/](https://explorer.monero-otc.com/) - https enabled.
- [http://monerochain.com/](http://monerochain.com/) - JSON API based, multiple nodes. - [http://monerochain.com/](http://monerochain.com/) - JSON API based, multiple nodes.
- [http://atesti.mooo.com:8081/](http://atesti.mooo.com:8081/) - Proof of Existence built with Monero and IPFS. - [http://atesti.mooo.com:8081/](http://atesti.mooo.com:8081/) - Proof of Existence built with Monero and IPFS (down currently).
- [https://anunknownamount.com](https://anunknownamount.com/) - looks nice, https enabled. - [https://anunknownamount.com](https://anunknownamount.com/) - looks nice, https enabled (down currently).
Clearnet testnet Monero version: Clearnet testnet Monero version:
- [https://retineo.serveo.net](https://retineo.serveo.net) - bleeding edge version (v8). - [http://nimis.serveo.net/](http://nimis.serveo.net/) - bleeding edge version (down currently).
- [https://testnet.xmrchain.com/](https://testnet.xmrchain.com/) - https enabled. - [https://testnet.xmrchain.com/](https://testnet.xmrchain.com/) - https enabled.
- [https://explorer.monero-otc.com/](https://explorer.monero-otc.com/) - https enabled. - [https://explorer.monero-otc.com/](https://explorer.monero-otc.com/) - https enabled.
- [https://testnet.MoneroExplorer.com/](https://testnet.moneroexplorer.com/) - https enabled. - [https://testnet.MoneroExplorer.com/](https://testnet.moneroexplorer.com/) - https enabled (down currently).
Clearnet stagenet Monero version:
- [http://162.210.173.150:8083/](http://162.210.173.150:8083/) - recent version.
i2p users (main Monero network): i2p users (main Monero network):
@ -62,7 +67,8 @@ Alternative block explorers:
The key features of the Onion Monero Blockchain Explorer are: The key features of the Onion Monero Blockchain Explorer are:
- no javascript, no cookies, no web analytics trackers, no images, - no cookies, no web analytics trackers, no images,
- by default no JavaScript, but can be enabled for client side decoding and proving transactions,
- open sourced, - open sourced,
- made fully in C++, - made fully in C++,
- showing encrypted payments ID, - showing encrypted payments ID,
@ -71,14 +77,14 @@ The key features of the Onion Monero Blockchain Explorer are:
- showing public components of Monero addresses, - showing public components of Monero addresses,
- decoding which outputs and mixins belong to the given Monero address and viewkey, - decoding which outputs and mixins belong to the given Monero address and viewkey,
- can prove that you send Monero to someone, - can prove that you send Monero to someone,
- detailed information about mixins, such as, mixins' - detailed information about mixins, such as, mixins' age, timescale, mixin of mixins,
age, timescale, mixin of mixins,
- showing number of amount output indices, - showing number of amount output indices,
- support Monero testnet network, - support Monero testnet network,
- tx checker and pusher for online pushing of transactions, - tx checker and pusher for online pushing of transactions,
- estimate possible spendings based on address and viewkey, - estimate possible spendings based on address and viewkey,
- can provide total amount of all miner fees. - can provide total amount of all miner fees.
- decoding encrypted payment id. - decoding encrypted payment id.
- decoding outputs and proving txs sent to sub-address.
## Development branch ## Development branch
@ -91,7 +97,7 @@ Current development branch, which includes support for sub-addresses is:
## Compilation on Ubuntu 16.04 ## Compilation on Ubuntu 16.04
##### Compile latest Monero release v0.11 ##### Compile latest Monero development version
Download and compile recent Monero into your home folder: Download and compile recent Monero into your home folder:
@ -99,17 +105,17 @@ Download and compile recent Monero into your home folder:
# first install monero dependecines # first install monero dependecines
sudo apt update sudo apt update
sudo apt install git build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libcurl4-openssl-dev libgtest-dev libreadline-dev libzmq3-dev sudo apt install git build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libcurl4-openssl-dev libgtest-dev libreadline-dev libzmq3-dev libsodium-dev
# go to home folder # go to home folder
cd ~ cd ~
git clone https://github.com/monero-project/monero git clone --recursive https://github.com/monero-project/monero
cd monero/ cd monero/
# checkout last monero version # checkout last monero version
git checkout -b last_release v0.11.1.0 #git checkout -b last_release v0.12.0.0
make make
``` ```
@ -137,6 +143,9 @@ cmake ..
# altearnatively can use: cmake -DMONERO_DIR=/path/to/monero_folder .. # altearnatively can use: cmake -DMONERO_DIR=/path/to/monero_folder ..
# if monero is not in ~/monero # if monero is not in ~/monero
#
# also can build with ASAN (sanitizers), for example
# cmake -DSANITIZE_ADDRESS=On ..
# compile # compile
make make
@ -170,6 +179,7 @@ Go to your browser: http://127.0.0.1:8081
xmrblocks, Onion Monero Blockchain Explorer: xmrblocks, Onion Monero Blockchain Explorer:
-h [ --help ] [=arg(=1)] (=0) produce help message -h [ --help ] [=arg(=1)] (=0) produce help message
-t [ --testnet ] [=arg(=1)] (=0) use testnet blockchain -t [ --testnet ] [=arg(=1)] (=0) use testnet blockchain
-s [ --stagenet ] [=arg(=1)] (=0) use stagenet blockchain
--enable-pusher [=arg(=1)] (=0) enable signed transaction pusher --enable-pusher [=arg(=1)] (=0) enable signed transaction pusher
--enable-mixin-details [=arg(=1)] (=0) --enable-mixin-details [=arg(=1)] (=0)
enable mixin details for key images, enable mixin details for key images,
@ -179,11 +189,13 @@ xmrblocks, Onion Monero Blockchain Explorer:
enable key images file checker enable key images file checker
--enable-output-key-checker [=arg(=1)] (=0) --enable-output-key-checker [=arg(=1)] (=0)
enable outputs key file checker enable outputs key file checker
--enable-json-api arg (=1) enable JSON REST api --enable-json-api [=arg(=1)] (=1) enable JSON REST api
--enable-tx-cache [=arg(=1)] (=0) enable caching of transaction details --enable-tx-cache [=arg(=1)] (=0) enable caching of transaction details
--show-cache-times [=arg(=1)] (=0) show times of getting data from cache --show-cache-times [=arg(=1)] (=0) show times of getting data from cache
vs no cache vs no cache
--enable-block-cache [=arg(=1)] (=0) enable caching of block details --enable-block-cache [=arg(=1)] (=0) enable caching of block details
--enable-js [=arg(=1)] (=0) enable checking outputs and proving txs
using JavaScript on client side
--enable-autorefresh-option [=arg(=1)] (=0) --enable-autorefresh-option [=arg(=1)] (=0)
enable users to have the index page on enable users to have the index page on
autorefresh autorefresh
@ -192,11 +204,14 @@ xmrblocks, Onion Monero Blockchain Explorer:
thread thread
-p [ --port ] arg (=8081) default explorer port -p [ --port ] arg (=8081) default explorer port
--testnet-url arg you can specify testnet url, if you run --testnet-url arg you can specify testnet url, if you run
it on mainnet. link will show on front it on mainnet or stagenet. link will
page to testnet explorer show on front page to testnet explorer
--stagenet-url arg you can specify stagenet url, if you
run it on mainnet or testnet. link will
show on front page to stagenet explorer
--mainnet-url arg you can specify mainnet url, if you run --mainnet-url arg you can specify mainnet url, if you run
it on testnet. link will show on front it on testnet or stagenet. link will
page to mainnet explorer show on front page to mainnet explorer
--no-blocks-on-index arg (=10) number of last blocks to be shown on --no-blocks-on-index arg (=10) number of last blocks to be shown on
index page index page
--mempool-info-timeout arg (=5000) maximum time, in milliseconds, to wait --mempool-info-timeout arg (=5000) maximum time, in milliseconds, to wait
@ -239,14 +254,14 @@ This flag will enable emission monitoring thread. When started, the thread
will initially scan the entire blockchain, and calculate the cumulative emission based on each block. will initially scan the entire blockchain, and calculate the cumulative emission based on each block.
Since it is a separate thread, the explorer will work as usual during this time. Since it is a separate thread, the explorer will work as usual during this time.
Every 10000 blocks, the thread will save current emission in a file, by default, Every 10000 blocks, the thread will save current emission in a file, by default,
in `~/.bitmonero/lmdb/emission_amount.txt`. For testnet network, in `~/.bitmonero/lmdb/emission_amount.txt`. For testnet or stagenet networks,
it is `~/.bitmonero/testnet/lmdb/emission_amount.txt`. This file is used so that we don't it is `~/.bitmonero/testnet/lmdb/emission_amount.txt` or `~/.bitmonero/stagenet/lmdb/emission_amount.txt`. This file is used so that we don't
need to rescan entire blockchain whenever the explorer is restarted. When the need to rescan entire blockchain whenever the explorer is restarted. When the
explorer restarts, the thread will first check if `~/.bitmonero/lmdb/emission_amount.txt` explorer restarts, the thread will first check if `~/.bitmonero/lmdb/emission_amount.txt`
is present, read its values, and continue from there if possible. Subsequently, only the initial is present, read its values, and continue from there if possible. Subsequently, only the initial
use of the tread is time consuming. Once the thread scans the entire blockchain, it updates use of the tread is time consuming. Once the thread scans the entire blockchain, it updates
the emission amount using new blocks as they come. Since the explorer writes this file, there can the emission amount using new blocks as they come. Since the explorer writes this file, there can
be only one instance of it running for mainnet and testnet. Thus, for example, you cant have be only one instance of it running for mainnet, testnet and stagenet. Thus, for example, you cant have
two explorers for mainnet two explorers for mainnet
running at the same time, as they will be trying to write and read the same file at the same time, running at the same time, as they will be trying to write and read the same file at the same time,
leading to unexpected results. Off course having one instance for mainnet and one instance for testnet leading to unexpected results. Off course having one instance for mainnet and one instance for testnet
@ -264,6 +279,15 @@ For example, for the above example: `print_coinbase_tx_sum 0 1313449`.
To disable the monitor, simply restart the explorer without `--enable-emission-monitor` flag. To disable the monitor, simply restart the explorer without `--enable-emission-monitor` flag.
## Enable JavaScript for decoding proving transactions
By default, decoding and proving tx's outputs are done on the server side. To do this on the client side
(private view and tx keys are not send to the server) JavaScript-based decoding can be enabled:
```
xmrblocks --enable-js
```
## Enable SSL (https) ## Enable SSL (https)
By default, the explorer does not use ssl. But it has such a functionality. By default, the explorer does not use ssl. But it has such a functionality.
@ -290,6 +314,11 @@ for any practical use need to have properly issued ssl certificates.
## JSON API ## JSON API
The explorer has JSON api. For the API, it uses conventions defined by [JSend](https://labs.omniti.com/labs/jsend). The explorer has JSON api. For the API, it uses conventions defined by [JSend](https://labs.omniti.com/labs/jsend).
By default the api is disabled. To enable it, use `--enable-json-api` flag, e.g.,
```
./xmrblocks --enable-json-api
```
#### api/transaction/<tx_hash> #### api/transaction/<tx_hash>

@ -0,0 +1,59 @@
# The MIT License (MIT)
#
# Copyright (c)
# 2013 Matthew Arsenault
# 2015-2016 RWTH Aachen University, Federal Republic of Germany
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off)
set(FLAG_CANDIDATES
# Clang 3.2+ use this version. The no-omit-frame-pointer option is optional.
"-g -fsanitize=address -fno-omit-frame-pointer"
"-g -fsanitize=address"
# Older deprecated flag for ASan
"-g -faddress-sanitizer"
)
if (SANITIZE_ADDRESS AND (SANITIZE_THREAD OR SANITIZE_MEMORY))
message(FATAL_ERROR "AddressSanitizer is not compatible with "
"ThreadSanitizer or MemorySanitizer.")
endif ()
include(sanitize-helpers)
if (SANITIZE_ADDRESS)
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer"
"ASan")
find_program(ASan_WRAPPER "asan-wrapper" PATHS ${CMAKE_MODULE_PATH})
mark_as_advanced(ASan_WRAPPER)
endif ()
function (add_sanitize_address TARGET)
if (NOT SANITIZE_ADDRESS)
return()
endif ()
sanitizer_add_flags(${TARGET} "AddressSanitizer" "ASan")
endfunction ()

@ -0,0 +1,57 @@
# The MIT License (MIT)
#
# Copyright (c)
# 2013 Matthew Arsenault
# 2015-2016 RWTH Aachen University, Federal Republic of Germany
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
option(SANITIZE_MEMORY "Enable MemorySanitizer for sanitized targets." Off)
set(FLAG_CANDIDATES
"-g -fsanitize=memory"
)
include(sanitize-helpers)
if (SANITIZE_MEMORY)
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
message(WARNING "MemorySanitizer disabled for target ${TARGET} because "
"MemorySanitizer is supported for Linux systems only.")
set(SANITIZE_MEMORY Off CACHE BOOL
"Enable MemorySanitizer for sanitized targets." FORCE)
elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8)
message(WARNING "MemorySanitizer disabled for target ${TARGET} because "
"MemorySanitizer is supported for 64bit systems only.")
set(SANITIZE_MEMORY Off CACHE BOOL
"Enable MemorySanitizer for sanitized targets." FORCE)
else ()
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "MemorySanitizer"
"MSan")
endif ()
endif ()
function (add_sanitize_memory TARGET)
if (NOT SANITIZE_MEMORY)
return()
endif ()
sanitizer_add_flags(${TARGET} "MemorySanitizer" "MSan")
endfunction ()

@ -28,9 +28,9 @@
# (c) 2014-2016 cpp-ethereum contributors. # (c) 2014-2016 cpp-ethereum contributors.
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
set(LIBS common;blocks;cryptonote_basic;cryptonote_core; set(LIBS common;blocks;cryptonote_basic;cryptonote_core;multisig;
cryptonote_protocol;daemonizer;mnemonics;epee;lmdb; cryptonote_protocol;daemonizer;mnemonics;epee;lmdb;device;
blockchain_db;ringct;wallet;cncrypto) blockchain_db;ringct;wallet;cncrypto;easylogging;version;checkpoints)
set(Xmr_INCLUDE_DIRS "${CPP_MONERO_DIR}") set(Xmr_INCLUDE_DIRS "${CPP_MONERO_DIR}")
@ -44,7 +44,7 @@ foreach (l ${LIBS})
find_library(Xmr_${L}_LIBRARY find_library(Xmr_${L}_LIBRARY
NAMES ${l} NAMES ${l}
PATHS ${CMAKE_LIBRARY_PATH} PATHS ${CMAKE_LIBRARY_PATH}
PATH_SUFFIXES "/src/${l}" "/external/db_drivers/lib${l}" "/lib" "/src/crypto" "/contrib/epee/src" PATH_SUFFIXES "/src/${l}" "/src/" "/external/db_drivers/lib${l}" "/lib" "/src/crypto" "/contrib/epee/src" "/external/easylogging++/"
NO_DEFAULT_PATH NO_DEFAULT_PATH
) )
@ -57,12 +57,14 @@ foreach (l ${LIBS})
endforeach() endforeach()
if (EXISTS ${MONERO_BUILD_DIR}/external/easylogging++/libeasylogging.a) if (EXISTS ${MONERO_BUILD_DIR}/src/ringct/libringct_basic.a)
add_library(easylogging STATIC IMPORTED) message(STATUS FindMonero " found libringct_basic.a")
set_property(TARGET easylogging add_library(ringct_basic STATIC IMPORTED)
PROPERTY IMPORTED_LOCATION ${MONERO_BUILD_DIR}/external/easylogging++/libeasylogging.a) set_property(TARGET ringct_basic
PROPERTY IMPORTED_LOCATION ${MONERO_BUILD_DIR}/src/ringct/libringct_basic.a)
endif() endif()
message(STATUS ${MONERO_SOURCE_DIR}/build) message(STATUS ${MONERO_SOURCE_DIR}/build)
# include monero headers # include monero headers

@ -0,0 +1,87 @@
# The MIT License (MIT)
#
# Copyright (c)
# 2013 Matthew Arsenault
# 2015-2016 RWTH Aachen University, Federal Republic of Germany
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# If any of the used compiler is a GNU compiler, add a second option to static
# link against the sanitizers.
option(SANITIZE_LINK_STATIC "Try to link static against sanitizers." Off)
set(FIND_QUIETLY_FLAG "")
if (DEFINED Sanitizers_FIND_QUIETLY)
set(FIND_QUIETLY_FLAG "QUIET")
endif ()
find_package(ASan ${FIND_QUIETLY_FLAG})
find_package(TSan ${FIND_QUIETLY_FLAG})
find_package(MSan ${FIND_QUIETLY_FLAG})
find_package(UBSan ${FIND_QUIETLY_FLAG})
function(sanitizer_add_blacklist_file FILE)
if(NOT IS_ABSOLUTE ${FILE})
set(FILE "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}")
endif()
get_filename_component(FILE "${FILE}" REALPATH)
sanitizer_check_compiler_flags("-fsanitize-blacklist=${FILE}"
"SanitizerBlacklist" "SanBlist")
endfunction()
function(add_sanitizers ...)
# If no sanitizer is enabled, return immediately.
if (NOT (SANITIZE_ADDRESS OR SANITIZE_MEMORY OR SANITIZE_THREAD OR
SANITIZE_UNDEFINED))
return()
endif ()
foreach (TARGET ${ARGV})
# Check if this target will be compiled by exactly one compiler. Other-
# wise sanitizers can't be used and a warning should be printed once.
sanitizer_target_compilers(${TARGET} TARGET_COMPILER)
list(LENGTH TARGET_COMPILER NUM_COMPILERS)
if (NUM_COMPILERS GREATER 1)
message(WARNING "Can't use any sanitizers for target ${TARGET}, "
"because it will be compiled by incompatible compilers. "
"Target will be compiled without sanitizers.")
return()
# If the target is compiled by no known compiler, ignore it.
elseif (NUM_COMPILERS EQUAL 0)
message(WARNING "Can't use any sanitizers for target ${TARGET}, "
"because it uses an unknown compiler. Target will be "
"compiled without sanitizers.")
return()
endif ()
# Add sanitizers for target.
add_sanitize_address(${TARGET})
add_sanitize_thread(${TARGET})
add_sanitize_memory(${TARGET})
add_sanitize_undefined(${TARGET})
endforeach ()
endfunction(add_sanitizers)

@ -0,0 +1,65 @@
# The MIT License (MIT)
#
# Copyright (c)
# 2013 Matthew Arsenault
# 2015-2016 RWTH Aachen University, Federal Republic of Germany
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
option(SANITIZE_THREAD "Enable ThreadSanitizer for sanitized targets." Off)
set(FLAG_CANDIDATES
"-g -fsanitize=thread"
)
# ThreadSanitizer is not compatible with MemorySanitizer.
if (SANITIZE_THREAD AND SANITIZE_MEMORY)
message(FATAL_ERROR "ThreadSanitizer is not compatible with "
"MemorySanitizer.")
endif ()
include(sanitize-helpers)
if (SANITIZE_THREAD)
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND
NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
message(WARNING "ThreadSanitizer disabled for target ${TARGET} because "
"ThreadSanitizer is supported for Linux systems and macOS only.")
set(SANITIZE_THREAD Off CACHE BOOL
"Enable ThreadSanitizer for sanitized targets." FORCE)
elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8)
message(WARNING "ThreadSanitizer disabled for target ${TARGET} because "
"ThreadSanitizer is supported for 64bit systems only.")
set(SANITIZE_THREAD Off CACHE BOOL
"Enable ThreadSanitizer for sanitized targets." FORCE)
else ()
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ThreadSanitizer"
"TSan")
endif ()
endif ()
function (add_sanitize_thread TARGET)
if (NOT SANITIZE_THREAD)
return()
endif ()
sanitizer_add_flags(${TARGET} "ThreadSanitizer" "TSan")
endfunction ()

@ -0,0 +1,46 @@
# The MIT License (MIT)
#
# Copyright (c)
# 2013 Matthew Arsenault
# 2015-2016 RWTH Aachen University, Federal Republic of Germany
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
option(SANITIZE_UNDEFINED
"Enable UndefinedBehaviorSanitizer for sanitized targets." Off)
set(FLAG_CANDIDATES
"-g -fsanitize=undefined"
)
include(sanitize-helpers)
if (SANITIZE_UNDEFINED)
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}"
"UndefinedBehaviorSanitizer" "UBSan")
endif ()
function (add_sanitize_undefined TARGET)
if (NOT SANITIZE_UNDEFINED)
return()
endif ()
sanitizer_add_flags(${TARGET} "UndefinedBehaviorSanitizer" "UBSan")
endfunction ()

@ -0,0 +1,55 @@
#!/bin/sh
# The MIT License (MIT)
#
# Copyright (c)
# 2013 Matthew Arsenault
# 2015-2016 RWTH Aachen University, Federal Republic of Germany
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# This script is a wrapper for AddressSanitizer. In some special cases you need
# to preload AddressSanitizer to avoid error messages - e.g. if you're
# preloading another library to your application. At the moment this script will
# only do something, if we're running on a Linux platform. OSX might not be
# affected.
# Exit immediately, if platform is not Linux.
if [ "$(uname)" != "Linux" ]
then
exec $@
fi
# Get the used libasan of the application ($1). If a libasan was found, it will
# be prepended to LD_PRELOAD.
libasan=$(ldd $1 | grep libasan | sed "s/^[[:space:]]//" | cut -d' ' -f1)
if [ -n "$libasan" ]
then
if [ -n "$LD_PRELOAD" ]
then
export LD_PRELOAD="$libasan:$LD_PRELOAD"
else
export LD_PRELOAD="$libasan"
fi
fi
# Execute the application.
exec $@

@ -0,0 +1,170 @@
# The MIT License (MIT)
#
# Copyright (c)
# 2013 Matthew Arsenault
# 2015-2016 RWTH Aachen University, Federal Republic of Germany
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# Helper function to get the language of a source file.
function (sanitizer_lang_of_source FILE RETURN_VAR)
get_filename_component(FILE_EXT "${FILE}" EXT)
string(TOLOWER "${FILE_EXT}" FILE_EXT)
string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT)
get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
foreach (LANG ${ENABLED_LANGUAGES})
list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP)
if (NOT ${TEMP} EQUAL -1)
set(${RETURN_VAR} "${LANG}" PARENT_SCOPE)
return()
endif ()
endforeach()
set(${RETURN_VAR} "" PARENT_SCOPE)
endfunction ()
# Helper function to get compilers used by a target.
function (sanitizer_target_compilers TARGET RETURN_VAR)
# Check if all sources for target use the same compiler. If a target uses
# e.g. C and Fortran mixed and uses different compilers (e.g. clang and
# gfortran) this can trigger huge problems, because different compilers may
# use different implementations for sanitizers.
set(BUFFER "")
get_target_property(TSOURCES ${TARGET} SOURCES)
foreach (FILE ${TSOURCES})
# If expression was found, FILE is a generator-expression for an object
# library. Object libraries will be ignored.
string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE})
if ("${_file}" STREQUAL "")
sanitizer_lang_of_source(${FILE} LANG)
if (LANG)
list(APPEND BUFFER ${CMAKE_${LANG}_COMPILER_ID})
endif ()
endif ()
endforeach ()
list(REMOVE_DUPLICATES BUFFER)
set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE)
endfunction ()
# Helper function to check compiler flags for language compiler.
function (sanitizer_check_compiler_flag FLAG LANG VARIABLE)
if (${LANG} STREQUAL "C")
include(CheckCCompilerFlag)
check_c_compiler_flag("${FLAG}" ${VARIABLE})
elseif (${LANG} STREQUAL "CXX")
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("${FLAG}" ${VARIABLE})
elseif (${LANG} STREQUAL "Fortran")
# CheckFortranCompilerFlag was introduced in CMake 3.x. To be compatible
# with older Cmake versions, we will check if this module is present
# before we use it. Otherwise we will define Fortran coverage support as
# not available.
include(CheckFortranCompilerFlag OPTIONAL RESULT_VARIABLE INCLUDED)
if (INCLUDED)
check_fortran_compiler_flag("${FLAG}" ${VARIABLE})
elseif (NOT CMAKE_REQUIRED_QUIET)
message(STATUS "Performing Test ${VARIABLE}")
message(STATUS "Performing Test ${VARIABLE}"
" - Failed (Check not supported)")
endif ()
endif()
endfunction ()
# Helper function to test compiler flags.
function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX)
set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY})
get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
foreach (LANG ${ENABLED_LANGUAGES})
# Sanitizer flags are not dependend on language, but the used compiler.
# So instead of searching flags foreach language, search flags foreach
# compiler used.
set(COMPILER ${CMAKE_${LANG}_COMPILER_ID})
if (NOT DEFINED ${PREFIX}_${COMPILER}_FLAGS)
foreach (FLAG ${FLAG_CANDIDATES})
if(NOT CMAKE_REQUIRED_QUIET)
message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]")
endif()
set(CMAKE_REQUIRED_FLAGS "${FLAG}")
unset(${PREFIX}_FLAG_DETECTED CACHE)
sanitizer_check_compiler_flag("${FLAG}" ${LANG}
${PREFIX}_FLAG_DETECTED)
if (${PREFIX}_FLAG_DETECTED)
# If compiler is a GNU compiler, search for static flag, if
# SANITIZE_LINK_STATIC is enabled.
if (SANITIZE_LINK_STATIC AND (${COMPILER} STREQUAL "GNU"))
string(TOLOWER ${PREFIX} PREFIX_lower)
sanitizer_check_compiler_flag(
"-static-lib${PREFIX_lower}" ${LANG}
${PREFIX}_STATIC_FLAG_DETECTED)
if (${PREFIX}_STATIC_FLAG_DETECTED)
set(FLAG "-static-lib${PREFIX_lower} ${FLAG}")
endif ()
endif ()
set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" CACHE STRING
"${NAME} flags for ${COMPILER} compiler.")
mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS)
break()
endif ()
endforeach ()
if (NOT ${PREFIX}_FLAG_DETECTED)
set(${PREFIX}_${COMPILER}_FLAGS "" CACHE STRING
"${NAME} flags for ${COMPILER} compiler.")
mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS)
message(WARNING "${NAME} is not available for ${COMPILER} "
"compiler. Targets using this compiler will be "
"compiled without ${NAME}.")
endif ()
endif ()
endforeach ()
endfunction ()
# Helper to assign sanitizer flags for TARGET.
function (sanitizer_add_flags TARGET NAME PREFIX)
# Get list of compilers used by target and check, if sanitizer is available
# for this target. Other compiler checks like check for conflicting
# compilers will be done in add_sanitizers function.
sanitizer_target_compilers(${TARGET} TARGET_COMPILER)
list(LENGTH TARGET_COMPILER NUM_COMPILERS)
if ("${${PREFIX}_${TARGET_COMPILER}_FLAGS}" STREQUAL "")
return()
endif()
# Set compile- and link-flags for target.
set_property(TARGET ${TARGET} APPEND_STRING
PROPERTY COMPILE_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}")
set_property(TARGET ${TARGET} APPEND_STRING
PROPERTY COMPILE_FLAGS " ${SanBlist_${TARGET_COMPILER}_FLAGS}")
set_property(TARGET ${TARGET} APPEND_STRING
PROPERTY LINK_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}")
endfunction ()

@ -1,229 +1,23 @@
#pragma once #pragma once
#include "crow/query_string.h"
#include <string> #include "crow/http_parser_merged.h"
#include <functional> #include "crow/ci_map.h"
#include <memory> #include "crow/TinySHA1.hpp"
#include <future> #include "crow/settings.h"
#include <cstdint> #include "crow/socket_adaptors.h"
#include <type_traits> #include "crow/json.h"
#include <thread> #include "crow/mustache.h"
#include "crow/logging.h"
#include "settings.h" #include "crow/dumb_timer_queue.h"
#include "logging.h" #include "crow/utility.h"
#include "utility.h" #include "crow/common.h"
#include "routing.h" #include "crow/http_request.h"
#include "middleware_context.h" #include "crow/websocket.h"
#include "http_request.h" #include "crow/parser.h"
#include "http_server.h" #include "crow/http_response.h"
#include "crow/middleware.h"
#include "crow/routing.h"
#ifdef CROW_MSVC_WORKAROUND #include "crow/middleware_context.h"
#define CROW_ROUTE(app, url) app.route_dynamic(url) #include "crow/http_connection.h"
#else #include "crow/http_server.h"
#define CROW_ROUTE(app, url) app.route<crow::black_magic::get_parameter_tag(url)>(url) #include "crow/app.h"
#endif
namespace crow
{
#ifdef CROW_ENABLE_SSL
using ssl_context_t = boost::asio::ssl::context;
#endif
template <typename ... Middlewares>
class Crow
{
public:
using self_t = Crow;
using server_t = Server<Crow, SocketAdaptor, Middlewares...>;
#ifdef CROW_ENABLE_SSL
using ssl_server_t = Server<Crow, SSLAdaptor, Middlewares...>;
#endif
Crow()
{
}
template <typename Adaptor>
void handle_upgrade(const request& req, response& res, Adaptor&& adaptor)
{
router_.handle_upgrade(req, res, adaptor);
}
void handle(const request& req, response& res)
{
router_.handle(req, res);
}
DynamicRule& route_dynamic(std::string&& rule)
{
return router_.new_rule_dynamic(std::move(rule));
}
template <uint64_t Tag>
auto route(std::string&& rule)
-> typename std::result_of<decltype(&Router::new_rule_tagged<Tag>)(Router, std::string&&)>::type
{
return router_.new_rule_tagged<Tag>(std::move(rule));
}
self_t& port(std::uint16_t port)
{
port_ = port;
return *this;
}
self_t& bindaddr(std::string bindaddr)
{
bindaddr_ = bindaddr;
return *this;
}
self_t& multithreaded()
{
return concurrency(std::thread::hardware_concurrency());
}
self_t& concurrency(std::uint16_t concurrency)
{
if (concurrency < 1)
concurrency = 1;
concurrency_ = concurrency;
return *this;
}
void validate()
{
router_.validate();
}
void run()
{
validate();
#ifdef CROW_ENABLE_SSL
if (use_ssl_)
{
ssl_server_ = std::move(std::unique_ptr<ssl_server_t>(new ssl_server_t(this, bindaddr_, port_, &middlewares_, concurrency_, &ssl_context_)));
ssl_server_->run();
}
else
#endif
{
server_ = std::move(std::unique_ptr<server_t>(new server_t(this, bindaddr_, port_, &middlewares_, concurrency_, nullptr)));
server_->run();
}
}
void stop()
{
#ifdef CROW_ENABLE_SSL
if (use_ssl_)
{
ssl_server_->stop();
}
else
#endif
{
server_->stop();
}
}
void debug_print()
{
CROW_LOG_DEBUG << "Routing:";
router_.debug_print();
}
#ifdef CROW_ENABLE_SSL
self_t& ssl_file(const std::string& crt_filename, const std::string& key_filename)
{
use_ssl_ = true;
ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer);
ssl_context_.use_certificate_file(crt_filename, ssl_context_t::pem);
ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem);
ssl_context_.set_options(
boost::asio::ssl::context::default_workarounds
| boost::asio::ssl::context::no_sslv2
| boost::asio::ssl::context::no_sslv3
);
return *this;
}
self_t& ssl_file(const std::string& pem_filename)
{
use_ssl_ = true;
ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer);
ssl_context_.load_verify_file(pem_filename);
ssl_context_.set_options(
boost::asio::ssl::context::default_workarounds
| boost::asio::ssl::context::no_sslv2
| boost::asio::ssl::context::no_sslv3
);
return *this;
}
self_t& ssl(boost::asio::ssl::context&& ctx)
{
use_ssl_ = true;
ssl_context_ = std::move(ctx);
return *this;
}
bool use_ssl_{false};
ssl_context_t ssl_context_{boost::asio::ssl::context::sslv23};
#else
template <typename T, typename ... Remain>
self_t& ssl_file(T&&, Remain&&...)
{
// We can't call .ssl() member function unless CROW_ENABLE_SSL is defined.
static_assert(
// make static_assert dependent to T; always false
std::is_base_of<T, void>::value,
"Define CROW_ENABLE_SSL to enable ssl support.");
return *this;
}
template <typename T>
self_t& ssl(T&&)
{
// We can't call .ssl() member function unless CROW_ENABLE_SSL is defined.
static_assert(
// make static_assert dependent to T; always false
std::is_base_of<T, void>::value,
"Define CROW_ENABLE_SSL to enable ssl support.");
return *this;
}
#endif
// middleware
using context_t = detail::context<Middlewares...>;
template <typename T>
typename T::context& get_context(const request& req)
{
static_assert(black_magic::contains<T, Middlewares...>::value, "App doesn't have the specified middleware type.");
auto& ctx = *reinterpret_cast<context_t*>(req.middleware_context);
return ctx.template get<T>();
}
template <typename T>
T& get_middleware()
{
return utility::get_element_by_type<T, Middlewares...>(middlewares_);
}
private:
uint16_t port_ = 80;
uint16_t concurrency_ = 1;
std::string bindaddr_ = "0.0.0.0";
Router router_;
std::tuple<Middlewares...> middlewares_;
#ifdef CROW_ENABLE_SSL
std::unique_ptr<ssl_server_t> ssl_server_;
#endif
std::unique_ptr<server_t> server_;
};
template <typename ... Middlewares>
using App = Crow<Middlewares...>;
using SimpleApp = Crow<>;
}

@ -0,0 +1,248 @@
#pragma once
#include <chrono>
#include <string>
#include <functional>
#include <memory>
#include <future>
#include <cstdint>
#include <type_traits>
#include <thread>
#include "crow/settings.h"
#include "crow/logging.h"
#include "crow/utility.h"
#include "crow/routing.h"
#include "crow/middleware_context.h"
#include "crow/http_request.h"
#include "crow/http_server.h"
#ifdef CROW_MSVC_WORKAROUND
#define CROW_ROUTE(app, url) app.route_dynamic(url)
#else
#define CROW_ROUTE(app, url) app.route<crow::black_magic::get_parameter_tag(url)>(url)
#endif
namespace crow
{
#ifdef CROW_ENABLE_SSL
using ssl_context_t = boost::asio::ssl::context;
#endif
template <typename ... Middlewares>
class Crow
{
public:
using self_t = Crow;
using server_t = Server<Crow, SocketAdaptor, Middlewares...>;
#ifdef CROW_ENABLE_SSL
using ssl_server_t = Server<Crow, SSLAdaptor, Middlewares...>;
#endif
Crow()
{
}
template <typename Adaptor>
void handle_upgrade(const request& req, response& res, Adaptor&& adaptor)
{
router_.handle_upgrade(req, res, adaptor);
}
void handle(const request& req, response& res)
{
router_.handle(req, res);
}
DynamicRule& route_dynamic(std::string&& rule)
{
return router_.new_rule_dynamic(std::move(rule));
}
template <uint64_t Tag>
auto route(std::string&& rule)
-> typename std::result_of<decltype(&Router::new_rule_tagged<Tag>)(Router, std::string&&)>::type
{
return router_.new_rule_tagged<Tag>(std::move(rule));
}
self_t& port(std::uint16_t port)
{
port_ = port;
return *this;
}
self_t& bindaddr(std::string bindaddr)
{
bindaddr_ = bindaddr;
return *this;
}
self_t& multithreaded()
{
return concurrency(std::thread::hardware_concurrency());
}
self_t& concurrency(std::uint16_t concurrency)
{
if (concurrency < 1)
concurrency = 1;
concurrency_ = concurrency;
return *this;
}
void validate()
{
router_.validate();
}
void run()
{
validate();
#ifdef CROW_ENABLE_SSL
if (use_ssl_)
{
ssl_server_ = std::move(std::unique_ptr<ssl_server_t>(new ssl_server_t(this, bindaddr_, port_, &middlewares_, concurrency_, &ssl_context_)));
ssl_server_->set_tick_function(tick_interval_, tick_function_);
ssl_server_->run();
}
else
#endif
{
server_ = std::move(std::unique_ptr<server_t>(new server_t(this, bindaddr_, port_, &middlewares_, concurrency_, nullptr)));
server_->set_tick_function(tick_interval_, tick_function_);
server_->run();
}
}
void stop()
{
#ifdef CROW_ENABLE_SSL
if (use_ssl_)
{
ssl_server_->stop();
}
else
#endif
{
server_->stop();
}
}
void debug_print()
{
CROW_LOG_DEBUG << "Routing:";
router_.debug_print();
}
self_t& loglevel(crow::LogLevel level)
{
crow::logger::setLogLevel(level);
return *this;
}
#ifdef CROW_ENABLE_SSL
self_t& ssl_file(const std::string& crt_filename, const std::string& key_filename)
{
use_ssl_ = true;
ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer);
ssl_context_.use_certificate_file(crt_filename, ssl_context_t::pem);
ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem);
ssl_context_.set_options(
boost::asio::ssl::context::default_workarounds
| boost::asio::ssl::context::no_sslv2
| boost::asio::ssl::context::no_sslv3
);
return *this;
}
self_t& ssl_file(const std::string& pem_filename)
{
use_ssl_ = true;
ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer);
ssl_context_.load_verify_file(pem_filename);
ssl_context_.set_options(
boost::asio::ssl::context::default_workarounds
| boost::asio::ssl::context::no_sslv2
| boost::asio::ssl::context::no_sslv3
);
return *this;
}
self_t& ssl(boost::asio::ssl::context&& ctx)
{
use_ssl_ = true;
ssl_context_ = std::move(ctx);
return *this;
}
bool use_ssl_{false};
ssl_context_t ssl_context_{boost::asio::ssl::context::sslv23};
#else
template <typename T, typename ... Remain>
self_t& ssl_file(T&&, Remain&&...)
{
// We can't call .ssl() member function unless CROW_ENABLE_SSL is defined.
static_assert(
// make static_assert dependent to T; always false
std::is_base_of<T, void>::value,
"Define CROW_ENABLE_SSL to enable ssl support.");
return *this;
}
template <typename T>
self_t& ssl(T&&)
{
// We can't call .ssl() member function unless CROW_ENABLE_SSL is defined.
static_assert(
// make static_assert dependent to T; always false
std::is_base_of<T, void>::value,
"Define CROW_ENABLE_SSL to enable ssl support.");
return *this;
}
#endif
// middleware
using context_t = detail::context<Middlewares...>;
template <typename T>
typename T::context& get_context(const request& req)
{
static_assert(black_magic::contains<T, Middlewares...>::value, "App doesn't have the specified middleware type.");
auto& ctx = *reinterpret_cast<context_t*>(req.middleware_context);
return ctx.template get<T>();
}
template <typename T>
T& get_middleware()
{
return utility::get_element_by_type<T, Middlewares...>(middlewares_);
}
template <typename Duration, typename Func>
self_t& tick(Duration d, Func f) {
tick_interval_ = std::chrono::duration_cast<std::chrono::milliseconds>(d);
tick_function_ = f;
return *this;
}
private:
uint16_t port_ = 80;
uint16_t concurrency_ = 1;
std::string bindaddr_ = "0.0.0.0";
Router router_;
std::chrono::milliseconds tick_interval_;
std::function<void()> tick_function_;
std::tuple<Middlewares...> middlewares_;
#ifdef CROW_ENABLE_SSL
std::unique_ptr<ssl_server_t> ssl_server_;
#endif
std::unique_ptr<server_t> server_;
};
template <typename ... Middlewares>
using App = Crow<Middlewares...>;
using SimpleApp = Crow<>;
}

@ -4,7 +4,7 @@
#include <string> #include <string>
#include <stdexcept> #include <stdexcept>
#include <iostream> #include <iostream>
#include "utility.h" #include "crow/utility.h"
namespace crow namespace crow
{ {
@ -19,6 +19,7 @@ namespace crow
CONNECT, CONNECT,
OPTIONS, OPTIONS,
TRACE, TRACE,
PATCH = 24,
#endif #endif
Delete = 0, Delete = 0,
@ -29,6 +30,7 @@ namespace crow
Connect, Connect,
Options, Options,
Trace, Trace,
Patch = 24,
}; };
inline std::string method_name(HTTPMethod method) inline std::string method_name(HTTPMethod method)
@ -51,6 +53,8 @@ namespace crow
return "OPTIONS"; return "OPTIONS";
case HTTPMethod::Trace: case HTTPMethod::Trace:
return "TRACE"; return "TRACE";
case HTTPMethod::Patch:
return "PATCH";
} }
return "invalid"; return "invalid";
} }
@ -132,6 +136,7 @@ constexpr crow::HTTPMethod operator "" _method(const char* str, size_t /*len*/)
crow::black_magic::is_equ_p(str, "OPTIONS", 7) ? crow::HTTPMethod::Options : crow::black_magic::is_equ_p(str, "OPTIONS", 7) ? crow::HTTPMethod::Options :
crow::black_magic::is_equ_p(str, "CONNECT", 7) ? crow::HTTPMethod::Connect : crow::black_magic::is_equ_p(str, "CONNECT", 7) ? crow::HTTPMethod::Connect :
crow::black_magic::is_equ_p(str, "TRACE", 5) ? crow::HTTPMethod::Trace : crow::black_magic::is_equ_p(str, "TRACE", 5) ? crow::HTTPMethod::Trace :
crow::black_magic::is_equ_p(str, "PATCH", 5) ? crow::HTTPMethod::Patch :
throw std::runtime_error("invalid http method"); throw std::runtime_error("invalid http method");
} }
#endif #endif

@ -6,7 +6,7 @@
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include "logging.h" #include "crow/logging.h"
namespace crow namespace crow
{ {

@ -7,15 +7,15 @@
#include <chrono> #include <chrono>
#include <vector> #include <vector>
#include "http_parser_merged.h" #include "crow/http_parser_merged.h"
#include "parser.h" #include "crow/parser.h"
#include "http_response.h" #include "crow/http_response.h"
#include "logging.h" #include "crow/logging.h"
#include "settings.h" #include "crow/settings.h"
#include "dumb_timer_queue.h" #include "crow/dumb_timer_queue.h"
#include "middleware_context.h" #include "crow/middleware_context.h"
#include "socket_adaptors.h" #include "crow/socket_adaptors.h"
namespace crow namespace crow
{ {
@ -176,7 +176,7 @@ namespace crow
} }
#ifdef CROW_ENABLE_DEBUG #ifdef CROW_ENABLE_DEBUG
static int connectionCount; static std::atomic<int> connectionCount;
#endif #endif
template <typename Adaptor, typename Handler, typename ... Middlewares> template <typename Adaptor, typename Handler, typename ... Middlewares>
class Connection class Connection
@ -374,6 +374,9 @@ namespace crow
{401, "HTTP/1.1 401 Unauthorized\r\n"}, {401, "HTTP/1.1 401 Unauthorized\r\n"},
{403, "HTTP/1.1 403 Forbidden\r\n"}, {403, "HTTP/1.1 403 Forbidden\r\n"},
{404, "HTTP/1.1 404 Not Found\r\n"}, {404, "HTTP/1.1 404 Not Found\r\n"},
{413, "HTTP/1.1 413 Payload Too Large\r\n"},
{422, "HTTP/1.1 422 Unprocessable Entity\r\n"},
{429, "HTTP/1.1 429 Too Many Requests\r\n"},
{500, "HTTP/1.1 500 Internal Server Error\r\n"}, {500, "HTTP/1.1 500 Internal Server Error\r\n"},
{501, "HTTP/1.1 501 Not Implemented\r\n"}, {501, "HTTP/1.1 501 Not Implemented\r\n"},
@ -467,7 +470,7 @@ namespace crow
if (!ec) if (!ec)
{ {
bool ret = parser_.feed(buffer_.data(), bytes_transferred); bool ret = parser_.feed(buffer_.data(), bytes_transferred);
if (ret && adaptor_.is_open() && !close_connection_) if (ret && adaptor_.is_open())
{ {
error_while_reading = false; error_while_reading = false;
} }
@ -482,6 +485,14 @@ namespace crow
CROW_LOG_DEBUG << this << " from read(1)"; CROW_LOG_DEBUG << this << " from read(1)";
check_destroy(); check_destroy();
} }
else if (close_connection_)
{
cancel_deadline_timer();
parser_.done();
is_reading = false;
check_destroy();
// adaptor will close after write
}
else if (!need_to_call_after_handlers_) else if (!need_to_call_after_handlers_)
{ {
start_deadline(); start_deadline();

@ -1,10 +1,11 @@
#pragma once #pragma once
#include "common.h"
#include "ci_map.h"
#include "query_string.h"
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "crow/common.h"
#include "crow/ci_map.h"
#include "crow/query_string.h"
namespace crow namespace crow
{ {
template <typename T> template <typename T>

@ -1,9 +1,10 @@
#pragma once #pragma once
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include "json.h"
#include "http_request.h" #include "crow/json.h"
#include "ci_map.h" #include "crow/http_request.h"
#include "crow/ci_map.h"
namespace crow namespace crow
{ {
@ -85,6 +86,12 @@ namespace crow
completed_ = false; completed_ = false;
} }
void redirect(const std::string& location)
{
code = 301;
set_header("Location", location);
}
void write(const std::string& body_part) void write(const std::string& body_part)
{ {
body += body_part; body += body_part;

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <chrono>
#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#ifdef CROW_ENABLE_SSL #ifdef CROW_ENABLE_SSL
@ -12,9 +13,9 @@
#include <memory> #include <memory>
#include "http_connection.h" #include "crow/http_connection.h"
#include "logging.h" #include "crow/logging.h"
#include "dumb_timer_queue.h" #include "crow/dumb_timer_queue.h"
namespace crow namespace crow
{ {
@ -28,6 +29,7 @@ namespace crow
Server(Handler* handler, std::string bindaddr, uint16_t port, std::tuple<Middlewares...>* middlewares = nullptr, uint16_t concurrency = 1, typename Adaptor::context* adaptor_ctx = nullptr) Server(Handler* handler, std::string bindaddr, uint16_t port, std::tuple<Middlewares...>* middlewares = nullptr, uint16_t concurrency = 1, typename Adaptor::context* adaptor_ctx = nullptr)
: acceptor_(io_service_, tcp::endpoint(boost::asio::ip::address::from_string(bindaddr), port)), : acceptor_(io_service_, tcp::endpoint(boost::asio::ip::address::from_string(bindaddr), port)),
signals_(io_service_, SIGINT, SIGTERM), signals_(io_service_, SIGINT, SIGTERM),
tick_timer_(io_service_),
handler_(handler), handler_(handler),
concurrency_(concurrency), concurrency_(concurrency),
port_(port), port_(port),
@ -37,6 +39,24 @@ namespace crow
{ {
} }
void set_tick_function(std::chrono::milliseconds d, std::function<void()> f)
{
tick_interval_ = d;
tick_function_ = f;
}
void on_tick()
{
tick_function_();
tick_timer_.expires_from_now(boost::posix_time::milliseconds(tick_interval_.count()));
tick_timer_.async_wait([this](const boost::system::error_code& ec)
{
if (ec)
return;
on_tick();
});
}
void run() void run()
{ {
if (concurrency_ < 0) if (concurrency_ < 0)
@ -101,15 +121,36 @@ namespace crow
timer.async_wait(handler); timer.async_wait(handler);
init_count ++; init_count ++;
while(1)
{
try try
{ {
io_service_pool_[i]->run(); if (io_service_pool_[i]->run() == 0)
{
// when io_service.run returns 0, there are no more works to do.
break;
}
} catch(std::exception& e) } catch(std::exception& e)
{ {
CROW_LOG_ERROR << "Worker Crash: An uncaught exception occurred: " << e.what(); CROW_LOG_ERROR << "Worker Crash: An uncaught exception occurred: " << e.what();
} }
}
})); }));
CROW_LOG_INFO << server_name_ << " server is running, local port " << port_;
if (tick_function_ && tick_interval_.count() > 0)
{
tick_timer_.expires_from_now(boost::posix_time::milliseconds(tick_interval_.count()));
tick_timer_.async_wait([this](const boost::system::error_code& ec)
{
if (ec)
return;
on_tick();
});
}
CROW_LOG_INFO << server_name_ << " server is running at " << bindaddr_ <<":" << port_
<< " using " << concurrency_ << " threads";
CROW_LOG_INFO << "Call `app.loglevel(crow::LogLevel::Warning)` to hide Info level logs.";
signals_.async_wait( signals_.async_wait(
[&](const boost::system::error_code& /*error*/, int /*signal_number*/){ [&](const boost::system::error_code& /*error*/, int /*signal_number*/){
@ -161,6 +202,10 @@ namespace crow
p->start(); p->start();
}); });
} }
else
{
delete p;
}
do_accept(); do_accept();
}); });
} }
@ -172,6 +217,7 @@ namespace crow
std::vector<std::function<std::string()>> get_cached_date_str_pool_; std::vector<std::function<std::string()>> get_cached_date_str_pool_;
tcp::acceptor acceptor_; tcp::acceptor acceptor_;
boost::asio::signal_set signals_; boost::asio::signal_set signals_;
boost::asio::deadline_timer tick_timer_;
Handler* handler_; Handler* handler_;
uint16_t concurrency_{1}; uint16_t concurrency_{1};
@ -180,6 +226,9 @@ namespace crow
std::string bindaddr_; std::string bindaddr_;
unsigned int roundrobin_index_{}; unsigned int roundrobin_index_{};
std::chrono::milliseconds tick_interval_;
std::function<void()> tick_function_;
std::tuple<Middlewares...>* middlewares_; std::tuple<Middlewares...>* middlewares_;
#ifdef CROW_ENABLE_SSL #ifdef CROW_ENABLE_SSL

@ -12,6 +12,8 @@
#include <boost/operators.hpp> #include <boost/operators.hpp>
#include <vector> #include <vector>
#include "crow/settings.h"
#if defined(__GNUG__) || defined(__clang__) #if defined(__GNUG__) || defined(__clang__)
#define crow_json_likely(x) __builtin_expect(x, 1) #define crow_json_likely(x) __builtin_expect(x, 1)
#define crow_json_unlikely(x) __builtin_expect(x, 0) #define crow_json_unlikely(x) __builtin_expect(x, 0)
@ -1262,6 +1264,23 @@ namespace crow
return *this; return *this;
} }
wvalue& operator=(std::vector<wvalue>&& v)
{
if (t_ != type::List)
reset();
t_ = type::List;
if (!l)
l = std::unique_ptr<std::vector<wvalue>>(new std::vector<wvalue>{});
l->clear();
l->resize(v.size());
size_t idx = 0;
for(auto& x:v)
{
(*l)[idx++] = std::move(x);
}
return *this;
}
template <typename T> template <typename T>
wvalue& operator=(const std::vector<T>& v) wvalue& operator=(const std::vector<T>& v)
{ {
@ -1314,6 +1333,18 @@ namespace crow
return (*o)[str]; return (*o)[str];
} }
std::vector<std::string> keys() const
{
if (t_ != type::Object)
return {};
std::vector<std::string> result;
for (auto& kv:*o)
{
result.push_back(kv.first);
}
return result;
}
size_t estimate_length() const size_t estimate_length() const
{ {
switch(t_) switch(t_)
@ -1354,7 +1385,6 @@ namespace crow
return 1; return 1;
} }
friend void dump_internal(const wvalue& v, std::string& out); friend void dump_internal(const wvalue& v, std::string& out);
friend std::string dump(const wvalue& v); friend std::string dump(const wvalue& v);
}; };
@ -1375,7 +1405,12 @@ namespace crow
case type::Number: case type::Number:
{ {
char outbuf[128]; char outbuf[128];
#ifdef _MSC_VER
sprintf_s(outbuf, 128, "%g", v.d);
#else
sprintf(outbuf, "%g", v.d); sprintf(outbuf, "%g", v.d);
#endif
out += outbuf; out += outbuf;
} }
break; break;

@ -7,7 +7,7 @@
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include "settings.h" #include "crow/settings.h"
namespace crow namespace crow
{ {

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <boost/algorithm/string/trim.hpp> #include <boost/algorithm/string/trim.hpp>
#include "http_request.h" #include "crow/http_request.h"
#include "http_response.h" #include "crow/http_response.h"
namespace crow namespace crow
{ {
@ -35,10 +35,11 @@ namespace crow
std::unordered_map<std::string, std::string> jar; std::unordered_map<std::string, std::string> jar;
std::unordered_map<std::string, std::string> cookies_to_add; std::unordered_map<std::string, std::string> cookies_to_add;
std::string get_cookie(const std::string& key) std::string get_cookie(const std::string& key) const
{ {
if (jar.count(key)) auto cookie = jar.find(key);
return jar[key]; if (cookie != jar.end())
return cookie->second;
return {}; return {};
} }
@ -73,69 +74,22 @@ namespace crow
if (pos == cookies.size()) if (pos == cookies.size())
break; break;
std::string value; size_t pos_semicolon = cookies.find(';', pos);
std::string value = cookies.substr(pos, pos_semicolon-pos);
if (cookies[pos] == '"')
{
int dquote_meet_count = 0;
pos ++;
size_t pos_dquote = pos-1;
do
{
pos_dquote = cookies.find('"', pos_dquote+1);
dquote_meet_count ++;
} while(pos_dquote < cookies.size() && cookies[pos_dquote-1] == '\\');
if (pos_dquote == cookies.npos)
break;
if (dquote_meet_count == 1) boost::trim(value);
value = cookies.substr(pos, pos_dquote - pos); if (value[0] == '"' && value[value.size()-1] == '"')
else
{
value.clear();
value.reserve(pos_dquote-pos);
for(size_t p = pos; p < pos_dquote; p++)
{
// FIXME minimal escaping
if (cookies[p] == '\\' && p + 1 < pos_dquote)
{
p++;
if (cookies[p] == '\\' || cookies[p] == '"')
value += cookies[p];
else
{ {
value += '\\'; value = value.substr(1, value.size()-2);
value += cookies[p];
}
}
else
value += cookies[p];
}
} }
ctx.jar.emplace(std::move(name), std::move(value)); ctx.jar.emplace(std::move(name), std::move(value));
pos = cookies.find(";", pos_dquote+1);
if (pos == cookies.npos)
break;
pos++;
while(pos < cookies.size() && cookies[pos] == ' ') pos++;
if (pos == cookies.size())
break;
}
else
{
size_t pos_semicolon = cookies.find(';', pos);
value = cookies.substr(pos, pos_semicolon - pos);
boost::trim(value);
ctx.jar.emplace(std::move(name), std::move(value));
pos = pos_semicolon; pos = pos_semicolon;
if (pos == cookies.npos) if (pos == cookies.npos)
break; break;
pos ++; pos++;
while(pos < cookies.size() && cookies[pos] == ' ') pos++; while(pos < cookies.size() && cookies[pos] == ' ') pos++;
if (pos == cookies.size())
break;
}
} }
} }
@ -143,6 +97,9 @@ namespace crow
{ {
for(auto& cookie:ctx.cookies_to_add) for(auto& cookie:ctx.cookies_to_add)
{ {
if (cookie.second.empty())
res.add_header("Set-Cookie", cookie.first + "=\"\"");
else
res.add_header("Set-Cookie", cookie.first + "=" + cookie.second); res.add_header("Set-Cookie", cookie.first + "=" + cookie.second);
} }
} }

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "utility.h" #include "crow/utility.h"
#include "http_request.h" #include "crow/http_request.h"
#include "http_response.h" #include "crow/http_response.h"
namespace crow namespace crow
{ {

@ -4,7 +4,7 @@
#include <fstream> #include <fstream>
#include <iterator> #include <iterator>
#include <functional> #include <functional>
#include "json.h" #include "crow/json.h"
namespace crow namespace crow
{ {
namespace mustache namespace mustache
@ -555,6 +555,11 @@ namespace crow
detail::get_loader_ref() = std::move(loader); detail::get_loader_ref() = std::move(loader);
} }
inline std::string load_text(const std::string& filename)
{
return detail::get_loader_ref()(filename);
}
inline template_t load(const std::string& filename) inline template_t load(const std::string& filename)
{ {
return compile(detail::get_loader_ref()(filename)); return compile(detail::get_loader_ref()(filename));

@ -3,11 +3,10 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/tokenizer.hpp>
#include <algorithm> #include <algorithm>
#include "http_parser_merged.h" #include "crow/http_parser_merged.h"
#include "http_request.h" #include "crow/http_request.h"
namespace crow namespace crow
{ {

@ -4,8 +4,12 @@
#include <string.h> #include <string.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <unordered_map>
#include <iostream> #include <iostream>
#include <boost/optional.hpp>
namespace crow
{
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// qs_parse (modified) // qs_parse (modified)
// https://github.com/bartgrantham/qs_parse // https://github.com/bartgrantham/qs_parse
@ -195,6 +199,48 @@ inline char * qs_k2v(const char * key, char * const * qs_kv, int qs_kv_size, int
return NULL; return NULL;
} }
inline boost::optional<std::pair<std::string, std::string>> qs_dict_name2kv(const char * dict_name, char * const * qs_kv, int qs_kv_size, int nth = 0)
{
int i;
size_t name_len, skip_to_eq, skip_to_brace_open, skip_to_brace_close;
name_len = strlen(dict_name);
#ifdef _qsSORTING
// TODO: binary search for key in the sorted qs_kv
#else // _qsSORTING
for(i=0; i<qs_kv_size; i++)
{
if ( strncmp(dict_name, qs_kv[i], name_len) == 0 )
{
skip_to_eq = strcspn(qs_kv[i], "=");
if ( qs_kv[i][skip_to_eq] == '=' )
skip_to_eq++;
skip_to_brace_open = strcspn(qs_kv[i], "[");
if ( qs_kv[i][skip_to_brace_open] == '[' )
skip_to_brace_open++;
skip_to_brace_close = strcspn(qs_kv[i], "]");
if ( skip_to_brace_open <= skip_to_brace_close &&
skip_to_brace_open > 0 &&
skip_to_brace_close > 0 &&
nth == 0 )
{
auto key = std::string(qs_kv[i] + skip_to_brace_open, skip_to_brace_close - skip_to_brace_open);
auto value = std::string(qs_kv[i] + skip_to_eq);
return boost::make_optional(std::make_pair(key, value));
}
else
{
--nth;
}
}
}
#endif // _qsSORTING
return boost::none;
}
inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t val_len) inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t val_len)
{ {
@ -220,7 +266,11 @@ inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t
{ {
qs++; qs++;
i = strcspn(qs, "&=#"); i = strcspn(qs, "&=#");
strncpy(val, qs, (val_len-1)<(i+1) ? (val_len-1) : (i+1)); #ifdef _MSC_VER
strncpy_s(val, val_len, qs, (val_len - 1)<(i + 1) ? (val_len - 1) : (i + 1));
#else
strncpy(val, qs, (val_len - 1)<(i + 1) ? (val_len - 1) : (i + 1));
#endif
qs_decode(val); qs_decode(val);
} }
else else
@ -231,6 +281,7 @@ inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t
return val; return val;
} }
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -333,6 +384,20 @@ namespace crow
return ret; return ret;
} }
std::unordered_map<std::string, std::string> get_dict (const std::string& name) const
{
std::unordered_map<std::string, std::string> ret;
int count = 0;
while(1)
{
if (auto element = qs_dict_name2kv(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size(), count++))
ret.insert(*element);
else
break;
}
return ret;
}
private: private:
std::string url_; std::string url_;

@ -8,12 +8,12 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <vector> #include <vector>
#include "common.h" #include "crow/common.h"
#include "http_response.h" #include "crow/http_response.h"
#include "http_request.h" #include "crow/http_request.h"
#include "utility.h" #include "crow/utility.h"
#include "logging.h" #include "crow/logging.h"
#include "websocket.h" #include "crow/websocket.h"
namespace crow namespace crow
{ {
@ -156,7 +156,7 @@ namespace crow
struct Wrapped struct Wrapped
{ {
template <typename ... Args> template <typename ... Args>
void set2(Func f, typename std::enable_if< void set_(Func f, typename std::enable_if<
!std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value !std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value
, int>::type = 0) , int>::type = 0)
{ {
@ -190,7 +190,7 @@ namespace crow
}; };
template <typename ... Args> template <typename ... Args>
void set2(Func f, typename std::enable_if< void set_(Func f, typename std::enable_if<
std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value && std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value &&
!std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value !std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value
, int>::type = 0) , int>::type = 0)
@ -205,7 +205,7 @@ namespace crow
} }
template <typename ... Args> template <typename ... Args>
void set2(Func f, typename std::enable_if< void set_(Func f, typename std::enable_if<
std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value && std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value &&
std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value
, int>::type = 0) , int>::type = 0)
@ -276,12 +276,12 @@ namespace crow
void handle_upgrade(const request& req, response&, SocketAdaptor&& adaptor) override void handle_upgrade(const request& req, response&, SocketAdaptor&& adaptor) override
{ {
new crow::websocket::Connection<SocketAdaptor>(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_); new crow::websocket::Connection<SocketAdaptor>(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
} }
#ifdef CROW_ENABLE_SSL #ifdef CROW_ENABLE_SSL
void handle_upgrade(const request& req, response&, SSLAdaptor&& adaptor) override void handle_upgrade(const request& req, response&, SSLAdaptor&& adaptor) override
{ {
new crow::websocket::Connection<SSLAdaptor>(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_); new crow::websocket::Connection<SSLAdaptor>(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
} }
#endif #endif
@ -313,11 +313,19 @@ namespace crow
return *this; return *this;
} }
template <typename Func>
self_t& onaccept(Func f)
{
accept_handler_ = f;
return *this;
}
protected: protected:
std::function<void(crow::websocket::connection&)> open_handler_; std::function<void(crow::websocket::connection&)> open_handler_;
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_; std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
std::function<void(crow::websocket::connection&, const std::string&)> close_handler_; std::function<void(crow::websocket::connection&, const std::string&)> close_handler_;
std::function<void(crow::websocket::connection&)> error_handler_; std::function<void(crow::websocket::connection&)> error_handler_;
std::function<bool(const crow::request&)> accept_handler_;
}; };
template <typename T> template <typename T>
@ -410,7 +418,9 @@ namespace crow
throw std::runtime_error("route_dynamic: Handler type is mismatched with URL parameters: " + rule_); throw std::runtime_error("route_dynamic: Handler type is mismatched with URL parameters: " + rule_);
} }
auto ret = detail::routing_handler_call_helper::Wrapped<Func, typename function_t::template arg<Indices>...>(); auto ret = detail::routing_handler_call_helper::Wrapped<Func, typename function_t::template arg<Indices>...>();
ret.template set2<typename function_t::template arg<Indices>...>(std::move(f)); ret.template set_<
typename function_t::template arg<Indices>...
>(std::move(f));
return ret; return ret;
} }
@ -454,10 +464,16 @@ namespace crow
static_assert(!std::is_same<void, decltype(f(std::declval<Args>()...))>::value, static_assert(!std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
"Handler function cannot have void return type; valid return types: string, int, crow::resposne, crow::json::wvalue"); "Handler function cannot have void return type; valid return types: string, int, crow::resposne, crow::json::wvalue");
handler_ = [f = std::move(f)](const request&, response& res, Args ... args){ handler_ = (
#ifdef CROW_CAN_USE_CPP14
[f = std::move(f)]
#else
[f]
#endif
(const request&, response& res, Args ... args){
res = response(f(args...)); res = response(f(args...));
res.end(); res.end();
}; });
} }
template <typename Func> template <typename Func>
@ -473,10 +489,16 @@ namespace crow
static_assert(!std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<Args>()...))>::value, static_assert(!std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<Args>()...))>::value,
"Handler function cannot have void return type; valid return types: string, int, crow::resposne, crow::json::wvalue"); "Handler function cannot have void return type; valid return types: string, int, crow::resposne, crow::json::wvalue");
handler_ = [f = std::move(f)](const crow::request& req, crow::response& res, Args ... args){ handler_ = (
#ifdef CROW_CAN_USE_CPP14
[f = std::move(f)]
#else
[f]
#endif
(const crow::request& req, crow::response& res, Args ... args){
res = response(f(req, args...)); res = response(f(req, args...));
res.end(); res.end();
}; });
} }
template <typename Func> template <typename Func>
@ -981,7 +1003,6 @@ public:
auto found = trie_.find(req.url); auto found = trie_.find(req.url);
unsigned rule_index = found.first; unsigned rule_index = found.first;
CROW_LOG_DEBUG << "???" << rule_index;
if (!rule_index) if (!rule_index)
{ {

@ -3,21 +3,21 @@
// TODO - replace with runtime config. libucl? // TODO - replace with runtime config. libucl?
/* #ifdef - enables debug mode */ /* #ifdef - enables debug mode */
#define CROW_ENABLE_DEBUG //#define CROW_ENABLE_DEBUG
/* #ifdef - enables logging */ /* #ifdef - enables logging */
#define CROW_ENABLE_LOGGING #define CROW_ENABLE_LOGGING
/* #ifdef - enables SSL */ /* #ifdef - enables ssl */
//#define CROW_ENABLE_SSL //#define CROW_ENABLE_SSL
/* #define - specifies log level */ /* #define - specifies log level */
/* /*
DEBUG = 0 Debug = 0
INFO = 1 Info = 1
WARNING = 2 Warning = 2
ERROR = 3 Error = 3
CRITICAL = 4 Critical = 4
default to INFO default to INFO
*/ */

@ -3,7 +3,7 @@
#ifdef CROW_ENABLE_SSL #ifdef CROW_ENABLE_SSL
#include <boost/asio/ssl.hpp> #include <boost/asio/ssl.hpp>
#endif #endif
#include "settings.h" #include "crow/settings.h"
namespace crow namespace crow
{ {
using namespace boost; using namespace boost;

@ -8,6 +8,8 @@
#include <functional> #include <functional>
#include <string> #include <string>
#include "crow/settings.h"
namespace crow namespace crow
{ {
namespace black_magic namespace black_magic
@ -500,7 +502,7 @@ template <typename F, typename Set>
using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
}; };
std::string base64encode(const char* data, size_t size, const char* key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") inline static std::string base64encode(const char* data, size_t size, const char* key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
{ {
std::string ret; std::string ret;
ret.resize((size+2) / 3 * 4); ret.resize((size+2) / 3 * 4);
@ -536,7 +538,7 @@ template <typename F, typename Set>
return ret; return ret;
} }
std::string base64encode_urlsafe(const char* data, size_t size) inline static std::string base64encode_urlsafe(const char* data, size_t size)
{ {
return base64encode(data, size, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"); return base64encode(data, size, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
} }

@ -1,7 +1,9 @@
#pragma once #pragma once
#include "socket_adaptors.h" #include <boost/algorithm/string/predicate.hpp>
#include "http_request.h" #include <boost/array.hpp>
#include "TinySHA1.hpp" #include "crow/socket_adaptors.h"
#include "crow/http_request.h"
#include "crow/TinySHA1.hpp"
namespace crow namespace crow
{ {
@ -22,6 +24,12 @@ namespace crow
virtual void send_text(const std::string& msg) = 0; virtual void send_text(const std::string& msg) = 0;
virtual void close(const std::string& msg = "quit") = 0; virtual void close(const std::string& msg = "quit") = 0;
virtual ~connection(){} virtual ~connection(){}
void userdata(void* u) { userdata_ = u; }
void* userdata() { return userdata_; }
private:
void* userdata_;
}; };
template <typename Adaptor> template <typename Adaptor>
@ -32,15 +40,28 @@ namespace crow
std::function<void(crow::websocket::connection&)> open_handler, std::function<void(crow::websocket::connection&)> open_handler,
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler, std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler,
std::function<void(crow::websocket::connection&, const std::string&)> close_handler, std::function<void(crow::websocket::connection&, const std::string&)> close_handler,
std::function<void(crow::websocket::connection&)> error_handler) std::function<void(crow::websocket::connection&)> error_handler,
std::function<bool(const crow::request&)> accept_handler)
: adaptor_(std::move(adaptor)), open_handler_(std::move(open_handler)), message_handler_(std::move(message_handler)), close_handler_(std::move(close_handler)), error_handler_(std::move(error_handler)) : adaptor_(std::move(adaptor)), open_handler_(std::move(open_handler)), message_handler_(std::move(message_handler)), close_handler_(std::move(close_handler)), error_handler_(std::move(error_handler))
, accept_handler_(std::move(accept_handler))
{ {
if (req.get_header_value("upgrade") != "websocket") if (!boost::iequals(req.get_header_value("upgrade"), "websocket"))
{ {
adaptor.close(); adaptor.close();
delete this; delete this;
return; return;
} }
if (accept_handler_)
{
if (!accept_handler_(req))
{
adaptor.close();
delete this;
return;
}
}
// Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== // Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
// Sec-WebSocket-Version: 13 // Sec-WebSocket-Version: 13
std::string magic = req.get_header_value("Sec-WebSocket-Key") + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; std::string magic = req.get_header_value("Sec-WebSocket-Key") + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
@ -125,13 +146,13 @@ namespace crow
else if (size < 0x10000) else if (size < 0x10000)
{ {
buf[1] += 126; buf[1] += 126;
*(uint16_t*)(buf+2) = (uint16_t)size; *(uint16_t*)(buf+2) = htons((uint16_t)size);
return {buf, buf+4}; return {buf, buf+4};
} }
else else
{ {
buf[1] += 127; buf[1] += 127;
*(uint64_t*)(buf+2) = (uint64_t)size; *(uint64_t*)(buf+2) = ((1==htonl(1)) ? (uint64_t)size : ((uint64_t)htonl((size) & 0xFFFFFFFF) << 32) | htonl((size) >> 32));
return {buf, buf+10}; return {buf, buf+10};
} }
} }
@ -162,7 +183,12 @@ namespace crow
{ {
//boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&mini_header_, 1), //boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&mini_header_, 1),
adaptor_.socket().async_read_some(boost::asio::buffer(&mini_header_, 2), adaptor_.socket().async_read_some(boost::asio::buffer(&mini_header_, 2),
[this](const boost::system::error_code& ec, std::size_t bytes_transferred) [this](const boost::system::error_code& ec, std::size_t
#ifdef CROW_ENABLE_DEBUG
bytes_transferred
#endif
)
{ {
is_reading = false; is_reading = false;
mini_header_ = htons(mini_header_); mini_header_ = htons(mini_header_);
@ -205,11 +231,17 @@ namespace crow
case WebSocketReadState::Len16: case WebSocketReadState::Len16:
{ {
remaining_length_ = 0; remaining_length_ = 0;
boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&remaining_length_, 2), uint16_t remaining_length16_ = 0;
[this](const boost::system::error_code& ec, std::size_t bytes_transferred) boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&remaining_length16_, 2),
[this,&remaining_length16_](const boost::system::error_code& ec, std::size_t
#ifdef CROW_ENABLE_DEBUG
bytes_transferred
#endif
)
{ {
is_reading = false; is_reading = false;
remaining_length_ = ntohs(*(uint16_t*)&remaining_length_); remaining_length16_ = ntohs(remaining_length16_);
remaining_length_ = remaining_length16_;
#ifdef CROW_ENABLE_DEBUG #ifdef CROW_ENABLE_DEBUG
if (!ec && bytes_transferred != 2) if (!ec && bytes_transferred != 2)
{ {
@ -236,7 +268,11 @@ namespace crow
case WebSocketReadState::Len64: case WebSocketReadState::Len64:
{ {
boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&remaining_length_, 8), boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&remaining_length_, 8),
[this](const boost::system::error_code& ec, std::size_t bytes_transferred) [this](const boost::system::error_code& ec, std::size_t
#ifdef CROW_ENABLE_DEBUG
bytes_transferred
#endif
)
{ {
is_reading = false; is_reading = false;
remaining_length_ = ((1==ntohl(1)) ? (remaining_length_) : ((uint64_t)ntohl((remaining_length_) & 0xFFFFFFFF) << 32) | ntohl((remaining_length_) >> 32)); remaining_length_ = ((1==ntohl(1)) ? (remaining_length_) : ((uint64_t)ntohl((remaining_length_) & 0xFFFFFFFF) << 32) | ntohl((remaining_length_) >> 32));
@ -265,7 +301,11 @@ namespace crow
break; break;
case WebSocketReadState::Mask: case WebSocketReadState::Mask:
boost::asio::async_read(adaptor_.socket(), boost::asio::buffer((char*)&mask_, 4), boost::asio::async_read(adaptor_.socket(), boost::asio::buffer((char*)&mask_, 4),
[this](const boost::system::error_code& ec, std::size_t bytes_transferred) [this](const boost::system::error_code& ec, std::size_t
#ifdef CROW_ENABLE_DEBUG
bytes_transferred
#endif
)
{ {
is_reading = false; is_reading = false;
#ifdef CROW_ENABLE_DEBUG #ifdef CROW_ENABLE_DEBUG
@ -477,6 +517,7 @@ namespace crow
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_; std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
std::function<void(crow::websocket::connection&, const std::string&)> close_handler_; std::function<void(crow::websocket::connection&, const std::string&)> close_handler_;
std::function<void(crow::websocket::connection&)> error_handler_; std::function<void(crow::websocket::connection&)> error_handler_;
std::function<bool(const crow::request&)> accept_handler_;
}; };
} }
} }

@ -11,6 +11,7 @@
#include <regex> #include <regex>
using boost::filesystem::path; using boost::filesystem::path;
using xmreg::remove_bad_chars;
using namespace std; using namespace std;
@ -49,14 +50,17 @@ main(int ac, const char* av[])
auto ssl_key_file_opt = opts.get_option<string>("ssl-key-file"); auto ssl_key_file_opt = opts.get_option<string>("ssl-key-file");
auto no_blocks_on_index_opt = opts.get_option<string>("no-blocks-on-index"); auto no_blocks_on_index_opt = opts.get_option<string>("no-blocks-on-index");
auto testnet_url = opts.get_option<string>("testnet-url"); auto testnet_url = opts.get_option<string>("testnet-url");
auto stagenet_url = opts.get_option<string>("stagenet-url");
auto mainnet_url = opts.get_option<string>("mainnet-url"); auto mainnet_url = opts.get_option<string>("mainnet-url");
auto mempool_info_timeout_opt = opts.get_option<string>("mempool-info-timeout"); auto mempool_info_timeout_opt = opts.get_option<string>("mempool-info-timeout");
auto mempool_refresh_time_opt = opts.get_option<string>("mempool-refresh-time"); auto mempool_refresh_time_opt = opts.get_option<string>("mempool-refresh-time");
auto testnet_opt = opts.get_option<bool>("testnet"); auto testnet_opt = opts.get_option<bool>("testnet");
auto stagenet_opt = opts.get_option<bool>("stagenet");
auto enable_key_image_checker_opt = opts.get_option<bool>("enable-key-image-checker"); auto enable_key_image_checker_opt = opts.get_option<bool>("enable-key-image-checker");
auto enable_output_key_checker_opt = opts.get_option<bool>("enable-output-key-checker"); auto enable_output_key_checker_opt = opts.get_option<bool>("enable-output-key-checker");
auto enable_autorefresh_option_opt = opts.get_option<bool>("enable-autorefresh-option"); auto enable_autorefresh_option_opt = opts.get_option<bool>("enable-autorefresh-option");
auto enable_pusher_opt = opts.get_option<bool>("enable-pusher"); auto enable_pusher_opt = opts.get_option<bool>("enable-pusher");
auto enable_js_opt = opts.get_option<bool>("enable-js");
auto enable_mixin_details_opt = opts.get_option<bool>("enable-mixin-details"); auto enable_mixin_details_opt = opts.get_option<bool>("enable-mixin-details");
auto enable_json_api_opt = opts.get_option<bool>("enable-json-api"); auto enable_json_api_opt = opts.get_option<bool>("enable-json-api");
auto enable_tx_cache_opt = opts.get_option<bool>("enable-tx-cache"); auto enable_tx_cache_opt = opts.get_option<bool>("enable-tx-cache");
@ -67,7 +71,20 @@ main(int ac, const char* av[])
bool testnet {*testnet_opt}; bool testnet {*testnet_opt};
bool stagenet {*stagenet_opt};
if (testnet && stagenet)
{
cerr << "testnet and stagenet cannot be specified at the same time!" << endl;
return EXIT_FAILURE;
}
const cryptonote::network_type nettype = testnet ?
cryptonote::network_type::TESTNET : stagenet ?
cryptonote::network_type::STAGENET : cryptonote::network_type::MAINNET;
bool enable_pusher {*enable_pusher_opt}; bool enable_pusher {*enable_pusher_opt};
bool enable_js {*enable_js_opt};
bool enable_key_image_checker {*enable_key_image_checker_opt}; bool enable_key_image_checker {*enable_key_image_checker_opt};
bool enable_autorefresh_option {*enable_autorefresh_option_opt}; bool enable_autorefresh_option {*enable_autorefresh_option_opt};
bool enable_output_key_checker {*enable_output_key_checker_opt}; bool enable_output_key_checker {*enable_output_key_checker_opt};
@ -125,7 +142,7 @@ main(int ac, const char* av[])
// get blockchain path // get blockchain path
path blockchain_path; path blockchain_path;
if (!xmreg::get_blockchain_path(bc_path_opt, blockchain_path, testnet)) if (!xmreg::get_blockchain_path(bc_path_opt, blockchain_path, nettype))
{ {
cerr << "Error getting blockchain path." << endl; cerr << "Error getting blockchain path." << endl;
return EXIT_FAILURE; return EXIT_FAILURE;
@ -150,16 +167,15 @@ main(int ac, const char* av[])
string deamon_url {*deamon_url_opt}; string deamon_url {*deamon_url_opt};
if (testnet && deamon_url == "http:://127.0.0.1:18081") if (testnet && deamon_url == "http:://127.0.0.1:18081")
{
deamon_url = "http:://127.0.0.1:28081"; deamon_url = "http:://127.0.0.1:28081";
} if (stagenet && deamon_url == "http:://127.0.0.1:18081")
deamon_url = "http:://127.0.0.1:38081";
uint64_t mempool_info_timeout {5000}; uint64_t mempool_info_timeout {5000};
try try
{ {
mempool_info_timeout = boost::lexical_cast<uint64_t>(*mempool_info_timeout_opt); mempool_info_timeout = boost::lexical_cast<uint64_t>(*mempool_info_timeout_opt);
} }
catch (boost::bad_lexical_cast &e) catch (boost::bad_lexical_cast &e)
{ {
@ -187,8 +203,8 @@ main(int ac, const char* av[])
xmreg::CurrentBlockchainStatus::blockchain_path xmreg::CurrentBlockchainStatus::blockchain_path
= blockchain_path; = blockchain_path;
xmreg::CurrentBlockchainStatus::testnet xmreg::CurrentBlockchainStatus::nettype
= testnet; = nettype;
xmreg::CurrentBlockchainStatus::deamon_url xmreg::CurrentBlockchainStatus::deamon_url
= deamon_url; = deamon_url;
xmreg::CurrentBlockchainStatus::set_blockchain_variables( xmreg::CurrentBlockchainStatus::set_blockchain_variables(
@ -204,8 +220,8 @@ main(int ac, const char* av[])
xmreg::MempoolStatus::blockchain_path xmreg::MempoolStatus::blockchain_path
= blockchain_path; = blockchain_path;
xmreg::MempoolStatus::testnet xmreg::MempoolStatus::nettype
= testnet; = nettype;
xmreg::MempoolStatus::deamon_url xmreg::MempoolStatus::deamon_url
= deamon_url; = deamon_url;
xmreg::MempoolStatus::set_blockchain_variables( xmreg::MempoolStatus::set_blockchain_variables(
@ -236,8 +252,9 @@ main(int ac, const char* av[])
xmreg::page xmrblocks(&mcore, xmreg::page xmrblocks(&mcore,
core_storage, core_storage,
deamon_url, deamon_url,
testnet, nettype,
enable_pusher, enable_pusher,
enable_js,
enable_key_image_checker, enable_key_image_checker,
enable_output_key_checker, enable_output_key_checker,
enable_autorefresh_option, enable_autorefresh_option,
@ -248,6 +265,7 @@ main(int ac, const char* av[])
no_blocks_on_index, no_blocks_on_index,
mempool_info_timeout, mempool_info_timeout,
*testnet_url, *testnet_url,
*stagenet_url,
*mainnet_url); *mainnet_url);
// crow instance // crow instance
@ -276,21 +294,23 @@ main(int ac, const char* av[])
CROW_ROUTE(app, "/block/<string>") CROW_ROUTE(app, "/block/<string>")
([&](const crow::request& req, string block_hash) { ([&](const crow::request& req, string block_hash) {
return crow::response(xmrblocks.show_block(block_hash)); return crow::response(xmrblocks.show_block(remove_bad_chars(block_hash)));
}); });
CROW_ROUTE(app, "/tx/<string>") CROW_ROUTE(app, "/tx/<string>")
([&](const crow::request& req, string tx_hash) { ([&](const crow::request& req, string tx_hash) {
return crow::response(xmrblocks.show_tx(tx_hash)); return crow::response(xmrblocks.show_tx(remove_bad_chars(tx_hash)));
}); });
CROW_ROUTE(app, "/tx/<string>/<uint>") CROW_ROUTE(app, "/tx/<string>/<uint>")
([&](string tx_hash, uint16_t with_ring_signatures) { ([&](string tx_hash, uint16_t with_ring_signatures)
return xmrblocks.show_tx(tx_hash, with_ring_signatures); {
return xmrblocks.show_tx(remove_bad_chars(tx_hash), with_ring_signatures);
}); });
CROW_ROUTE(app, "/myoutputs").methods("POST"_method) CROW_ROUTE(app, "/myoutputs").methods("POST"_method)
([&](const crow::request& req) { ([&](const crow::request& req)
{
map<std::string, std::string> post_body map<std::string, std::string> post_body
= xmreg::parse_crow_post_data(req.body); = xmreg::parse_crow_post_data(req.body);
@ -302,13 +322,13 @@ main(int ac, const char* av[])
return string("xmr address, viewkey or tx hash not provided"); return string("xmr address, viewkey or tx hash not provided");
} }
string tx_hash = post_body["tx_hash"]; string tx_hash = remove_bad_chars(post_body["tx_hash"]);
string xmr_address = post_body["xmr_address"]; string xmr_address = remove_bad_chars(post_body["xmr_address"]);
string viewkey = post_body["viewkey"]; string viewkey = remove_bad_chars(post_body["viewkey"]);
// this will be only not empty when checking raw tx data // this will be only not empty when checking raw tx data
// using tx pusher // using tx pusher
string raw_tx_data = post_body["raw_tx_data"]; string raw_tx_data = remove_bad_chars(post_body["raw_tx_data"]);
string domain = get_domain(req); string domain = get_domain(req);
@ -319,12 +339,15 @@ main(int ac, const char* av[])
CROW_ROUTE(app, "/myoutputs/<string>/<string>/<string>") CROW_ROUTE(app, "/myoutputs/<string>/<string>/<string>")
([&](const crow::request& req, string tx_hash, ([&](const crow::request& req, string tx_hash,
string xmr_address, string viewkey) { string xmr_address, string viewkey)
{
string domain = get_domain(req); string domain = get_domain(req);
return xmrblocks.show_my_outputs(tx_hash, xmr_address, return xmrblocks.show_my_outputs(remove_bad_chars(tx_hash),
viewkey, string {}, remove_bad_chars(xmr_address),
remove_bad_chars(viewkey),
string {},
domain); domain);
}); });
@ -342,14 +365,21 @@ main(int ac, const char* av[])
"tx hash not provided"); "tx hash not provided");
} }
string tx_hash = post_body["txhash"];; string tx_hash = remove_bad_chars(post_body["txhash"]);
string tx_prv_key = post_body["txprvkey"];; string tx_prv_key = remove_bad_chars(post_body["txprvkey"]);
string xmr_address = post_body["xmraddress"];; string xmr_address = remove_bad_chars(post_body["xmraddress"]);
// this will be only not empty when checking raw tx data
// using tx pusher
string raw_tx_data = remove_bad_chars(post_body["raw_tx_data"]);
string domain = get_domain(req); string domain = get_domain(req);
return xmrblocks.show_prove(tx_hash, xmr_address, return xmrblocks.show_prove(tx_hash,
tx_prv_key, domain); xmr_address,
tx_prv_key,
raw_tx_data,
domain);
}); });
@ -359,8 +389,11 @@ main(int ac, const char* av[])
string domain = get_domain(req); string domain = get_domain(req);
return xmrblocks.show_prove(tx_hash, xmr_address, return xmrblocks.show_prove(remove_bad_chars(tx_hash),
tx_prv_key, domain); remove_bad_chars(xmr_address),
remove_bad_chars(tx_prv_key),
string {},
domain);
}); });
if (enable_pusher) if (enable_pusher)
@ -381,13 +414,14 @@ main(int ac, const char* av[])
return string("Raw tx data or action not provided"); return string("Raw tx data or action not provided");
} }
string raw_tx_data = post_body["rawtxdata"]; string raw_tx_data = remove_bad_chars(post_body["rawtxdata"]);
string action = post_body["action"]; string action = remove_bad_chars(post_body["action"]);
if (action == "check") if (action == "check")
return xmrblocks.show_checkrawtx(raw_tx_data, action); return xmrblocks.show_checkrawtx(raw_tx_data, action);
else if (action == "push") else if (action == "push")
return xmrblocks.show_pushrawtx(raw_tx_data, action); return xmrblocks.show_pushrawtx(raw_tx_data, action);
return string("Provided action is neither check nor push");
}); });
} }
@ -415,8 +449,8 @@ main(int ac, const char* av[])
return string("Viewkey not provided. Cant decrypt key image file without it"); return string("Viewkey not provided. Cant decrypt key image file without it");
} }
string raw_data = post_body["rawkeyimgsdata"]; string raw_data = remove_bad_chars(post_body["rawkeyimgsdata"]);
string viewkey = post_body["viewkey"]; string viewkey = remove_bad_chars(post_body["viewkey"]);
return xmrblocks.show_checkrawkeyimgs(raw_data, viewkey); return xmrblocks.show_checkrawkeyimgs(raw_data, viewkey);
}); });
@ -447,8 +481,8 @@ main(int ac, const char* av[])
"key image file without it"); "key image file without it");
} }
string raw_data = post_body["rawoutputkeysdata"]; string raw_data = remove_bad_chars(post_body["rawoutputkeysdata"]);
string viewkey = post_body["viewkey"]; string viewkey = remove_bad_chars(post_body["viewkey"]);
return xmrblocks.show_checkcheckrawoutput(raw_data, viewkey); return xmrblocks.show_checkcheckrawoutput(raw_data, viewkey);
}); });
@ -457,7 +491,7 @@ main(int ac, const char* av[])
CROW_ROUTE(app, "/search").methods("GET"_method) CROW_ROUTE(app, "/search").methods("GET"_method)
([&](const crow::request& req) { ([&](const crow::request& req) {
return xmrblocks.search(string(req.url_params.get("value"))); return xmrblocks.search(remove_bad_chars(string(req.url_params.get("value"))));
}); });
CROW_ROUTE(app, "/mempool") CROW_ROUTE(app, "/mempool")
@ -483,12 +517,73 @@ main(int ac, const char* av[])
return text; return text;
}); });
if (enable_js)
{
cout << "Enable JavaScript checking of outputs and proving txs\n";
CROW_ROUTE(app, "/js/jquery.min.js")
([&](const crow::request& req) {
return xmrblocks.get_js_file("jquery.min.js");
});
CROW_ROUTE(app, "/js/crc32.js")
([&](const crow::request& req) {
return xmrblocks.get_js_file("crc32.js");
});
CROW_ROUTE(app, "/js/biginteger.js")
([&](const crow::request& req) {
return xmrblocks.get_js_file("biginteger.js");
});
CROW_ROUTE(app, "/js/crypto.js")
([&](const crow::request& req) {
return xmrblocks.get_js_file("crypto.js");
});
CROW_ROUTE(app, "/js/config.js")
([&](const crow::request& req) {
return xmrblocks.get_js_file("config.js");
});
CROW_ROUTE(app, "/js/nacl-fast-cn.js")
([&](const crow::request& req) {
return xmrblocks.get_js_file("nacl-fast-cn.js");
});
CROW_ROUTE(app, "/js/base58.js")
([&](const crow::request& req) {
return xmrblocks.get_js_file("base58.js");
});
CROW_ROUTE(app, "/js/cn_util.js")
([&](const crow::request& req) {
return xmrblocks.get_js_file("cn_util.js");
});
CROW_ROUTE(app, "/js/sha3.js")
([&](const crow::request& req) {
return xmrblocks.get_js_file("sha3.js");
});
CROW_ROUTE(app, "/js/all_in_one.js")
([&](const crow::request& req) {
// /js/all_in_one.js file does not exist. it is generated on the fly
// from the above real files.
return xmrblocks.get_js_file("all_in_one.js");
});
} // if (enable_js)
if (enable_json_api) if (enable_json_api)
{ {
cout << "Enable JSON API\n";
CROW_ROUTE(app, "/api/transaction/<string>") CROW_ROUTE(app, "/api/transaction/<string>")
([&](const crow::request &req, string tx_hash) { ([&](const crow::request &req, string tx_hash) {
myxmr::jsonresponse r{xmrblocks.json_transaction(tx_hash)}; myxmr::jsonresponse r{xmrblocks.json_transaction(remove_bad_chars(tx_hash))};
return r; return r;
}); });
@ -496,7 +591,7 @@ main(int ac, const char* av[])
CROW_ROUTE(app, "/api/rawtransaction/<string>") CROW_ROUTE(app, "/api/rawtransaction/<string>")
([&](const crow::request &req, string tx_hash) { ([&](const crow::request &req, string tx_hash) {
myxmr::jsonresponse r{xmrblocks.json_rawtransaction(tx_hash)}; myxmr::jsonresponse r{xmrblocks.json_rawtransaction(remove_bad_chars(tx_hash))};
return r; return r;
}); });
@ -504,7 +599,7 @@ main(int ac, const char* av[])
CROW_ROUTE(app, "/api/block/<string>") CROW_ROUTE(app, "/api/block/<string>")
([&](const crow::request &req, string block_no_or_hash) { ([&](const crow::request &req, string block_no_or_hash) {
myxmr::jsonresponse r{xmrblocks.json_block(block_no_or_hash)}; myxmr::jsonresponse r{xmrblocks.json_block(remove_bad_chars(block_no_or_hash))};
return r; return r;
}); });
@ -512,7 +607,7 @@ main(int ac, const char* av[])
CROW_ROUTE(app, "/api/rawblock/<string>") CROW_ROUTE(app, "/api/rawblock/<string>")
([&](const crow::request &req, string block_no_or_hash) { ([&](const crow::request &req, string block_no_or_hash) {
myxmr::jsonresponse r{xmrblocks.json_rawblock(block_no_or_hash)}; myxmr::jsonresponse r{xmrblocks.json_rawblock(remove_bad_chars(block_no_or_hash))};
return r; return r;
}); });
@ -526,7 +621,8 @@ main(int ac, const char* av[])
string limit = regex_search(req.raw_url, regex {"limit=\\d+"}) ? string limit = regex_search(req.raw_url, regex {"limit=\\d+"}) ?
req.url_params.get("limit") : "25"; req.url_params.get("limit") : "25";
myxmr::jsonresponse r{xmrblocks.json_transactions(page, limit)}; myxmr::jsonresponse r{xmrblocks.json_transactions(
remove_bad_chars(page), remove_bad_chars(limit))};
return r; return r;
}); });
@ -543,7 +639,8 @@ main(int ac, const char* av[])
string limit = regex_search(req.raw_url, regex {"limit=\\d+"}) ? string limit = regex_search(req.raw_url, regex {"limit=\\d+"}) ?
req.url_params.get("limit") : "100000000"; req.url_params.get("limit") : "100000000";
myxmr::jsonresponse r{xmrblocks.json_mempool(page, limit)}; myxmr::jsonresponse r{xmrblocks.json_mempool(
remove_bad_chars(page), remove_bad_chars(limit))};
return r; return r;
}); });
@ -551,7 +648,7 @@ main(int ac, const char* av[])
CROW_ROUTE(app, "/api/search/<string>") CROW_ROUTE(app, "/api/search/<string>")
([&](const crow::request &req, string search_value) { ([&](const crow::request &req, string search_value) {
myxmr::jsonresponse r{xmrblocks.json_search(search_value)}; myxmr::jsonresponse r{xmrblocks.json_search(remove_bad_chars(search_value))};
return r; return r;
}); });
@ -597,7 +694,11 @@ main(int ac, const char* av[])
cerr << "Cant parse tx_prove as bool. Using default value" << endl; cerr << "Cant parse tx_prove as bool. Using default value" << endl;
} }
myxmr::jsonresponse r{xmrblocks.json_outputs(tx_hash, address, viewkey, tx_prove)}; myxmr::jsonresponse r{xmrblocks.json_outputs(
remove_bad_chars(tx_hash),
remove_bad_chars(address),
remove_bad_chars(viewkey),
tx_prove)};
return r; return r;
}); });
@ -627,7 +728,11 @@ main(int ac, const char* av[])
cerr << "Cant parse tx_prove as bool. Using default value" << endl; cerr << "Cant parse tx_prove as bool. Using default value" << endl;
} }
myxmr::jsonresponse r{xmrblocks.json_outputsblocks(limit, address, viewkey, in_mempool_aswell)}; myxmr::jsonresponse r{xmrblocks.json_outputsblocks(
remove_bad_chars(limit),
remove_bad_chars(address),
remove_bad_chars(viewkey),
in_mempool_aswell)};
return r; return r;
}); });
@ -640,7 +745,7 @@ main(int ac, const char* av[])
return r; return r;
}); });
} } // if (enable_json_api)
if (enable_autorefresh_option) if (enable_autorefresh_option)
{ {

@ -25,6 +25,8 @@ namespace xmreg
"produce help message") "produce help message")
("testnet,t", value<bool>()->default_value(false)->implicit_value(true), ("testnet,t", value<bool>()->default_value(false)->implicit_value(true),
"use testnet blockchain") "use testnet blockchain")
("stagenet,s", value<bool>()->default_value(false)->implicit_value(true),
"use stagenet blockchain")
("enable-pusher", value<bool>()->default_value(false)->implicit_value(true), ("enable-pusher", value<bool>()->default_value(false)->implicit_value(true),
"enable signed transaction pusher") "enable signed transaction pusher")
("enable-mixin-details", value<bool>()->default_value(false)->implicit_value(true), ("enable-mixin-details", value<bool>()->default_value(false)->implicit_value(true),
@ -33,7 +35,7 @@ namespace xmreg
"enable key images file checker") "enable key images file checker")
("enable-output-key-checker", value<bool>()->default_value(false)->implicit_value(true), ("enable-output-key-checker", value<bool>()->default_value(false)->implicit_value(true),
"enable outputs key file checker") "enable outputs key file checker")
("enable-json-api", value<bool>()->default_value(true), ("enable-json-api", value<bool>()->default_value(false)->implicit_value(true),
"enable JSON REST api") "enable JSON REST api")
("enable-tx-cache", value<bool>()->default_value(false)->implicit_value(true), ("enable-tx-cache", value<bool>()->default_value(false)->implicit_value(true),
"enable caching of transaction details") "enable caching of transaction details")
@ -41,6 +43,8 @@ namespace xmreg
"show times of getting data from cache vs no cache") "show times of getting data from cache vs no cache")
("enable-block-cache", value<bool>()->default_value(false)->implicit_value(true), ("enable-block-cache", value<bool>()->default_value(false)->implicit_value(true),
"enable caching of block details") "enable caching of block details")
("enable-js", value<bool>()->default_value(false)->implicit_value(true),
"enable checking outputs and proving txs using JavaScript on client side")
("enable-autorefresh-option", value<bool>()->default_value(false)->implicit_value(true), ("enable-autorefresh-option", value<bool>()->default_value(false)->implicit_value(true),
"enable users to have the index page on autorefresh") "enable users to have the index page on autorefresh")
("enable-emission-monitor", value<bool>()->default_value(false)->implicit_value(true), ("enable-emission-monitor", value<bool>()->default_value(false)->implicit_value(true),
@ -48,9 +52,11 @@ namespace xmreg
("port,p", value<string>()->default_value("8081"), ("port,p", value<string>()->default_value("8081"),
"default explorer port") "default explorer port")
("testnet-url", value<string>()->default_value(""), ("testnet-url", value<string>()->default_value(""),
"you can specify testnet url, if you run it on mainnet. link will show on front page to testnet explorer") "you can specify testnet url, if you run it on mainnet or stagenet. link will show on front page to testnet explorer")
("stagenet-url", value<string>()->default_value(""),
"you can specify stagenet url, if you run it on mainnet or testnet. link will show on front page to stagenet explorer")
("mainnet-url", value<string>()->default_value(""), ("mainnet-url", value<string>()->default_value(""),
"you can specify mainnet url, if you run it on testnet. link will show on front page to mainnet explorer") "you can specify mainnet url, if you run it on testnet or stagenet. link will show on front page to mainnet explorer")
("no-blocks-on-index", value<string>()->default_value("10"), ("no-blocks-on-index", value<string>()->default_value("10"),
"number of last blocks to be shown on index page") "number of last blocks to be shown on index page")
("mempool-info-timeout", value<string>()->default_value("5000"), ("mempool-info-timeout", value<string>()->default_value("5000"),

@ -299,7 +299,7 @@ CurrentBlockchainStatus::is_thread_running()
bf::path CurrentBlockchainStatus::blockchain_path {"/home/mwo/.bitmonero/lmdb"}; bf::path CurrentBlockchainStatus::blockchain_path {"/home/mwo/.bitmonero/lmdb"};
bool CurrentBlockchainStatus::testnet {false}; cryptonote::network_type CurrentBlockchainStatus::nettype {cryptonote::network_type::MAINNET};
string CurrentBlockchainStatus::output_file {"emission_amount.txt"}; string CurrentBlockchainStatus::output_file {"emission_amount.txt"};

@ -47,7 +47,7 @@ struct CurrentBlockchainStatus
static bf::path blockchain_path; static bf::path blockchain_path;
static bool testnet; static cryptonote::network_type nettype;
static string output_file; static string output_file;

@ -58,7 +58,7 @@ MempoolStatus::start_mempool_status_thread()
else else
{ {
cout << "Current network info read, "; cout << "Current network info read, ";
loop_index == 0; loop_index = 0;
} }
} }
@ -126,32 +126,20 @@ MempoolStatus::read_mempool()
// get transaction info of the tx in the mempool // get transaction info of the tx in the mempool
const tx_info& _tx_info = mempool_tx_info.at(i); const tx_info& _tx_info = mempool_tx_info.at(i);
crypto::hash mem_tx_hash = null_hash;
if (epee::string_tools::hex_to_pod(_tx_info.id_hash, mem_tx_hash))
{
transaction tx; transaction tx;
crypto::hash tx_hash;
crypto::hash tx_prefix_hash;
if (!xmreg::make_tx_from_json(_tx_info.tx_json, tx)) if (!parse_and_validate_tx_from_blob(
_tx_info.tx_blob, tx, tx_hash, tx_prefix_hash))
{ {
cerr << "Cant make tx from _tx_info.tx_json" << endl; cerr << "Cant make tx from _tx_info.tx_blob" << endl;
return false;
}
crypto::hash tx_hash_reconstructed = get_transaction_hash(tx);
if (mem_tx_hash != tx_hash_reconstructed)
{
cerr << "Hash of reconstructed tx from json does not match "
"what we should get!"
<< endl;
return false; return false;
} }
mempool_size_kB += _tx_info.blob_size; mempool_size_kB += _tx_info.blob_size;
local_copy_of_mempool_txs.push_back(mempool_tx {tx_hash_reconstructed, tx}); local_copy_of_mempool_txs.push_back(mempool_tx {tx_hash, tx});
mempool_tx& last_tx = local_copy_of_mempool_txs.back(); mempool_tx& last_tx = local_copy_of_mempool_txs.back();
@ -165,6 +153,12 @@ MempoolStatus::read_mempool()
const array<uint64_t, 4>& sum_data = summary_of_in_out_rct( const array<uint64_t, 4>& sum_data = summary_of_in_out_rct(
tx, output_pub_keys, input_key_imgs); tx, output_pub_keys, input_key_imgs);
double tx_size = static_cast<double>(_tx_info.blob_size)/1024.0;
double payed_for_kB = XMR_AMOUNT(_tx_info.fee) / tx_size;
last_tx.receive_time = _tx_info.receive_time; last_tx.receive_time = _tx_info.receive_time;
last_tx.sum_outputs = sum_data[0]; last_tx.sum_outputs = sum_data[0];
@ -175,14 +169,32 @@ MempoolStatus::read_mempool()
last_tx.num_nonrct_inputs = sum_data[3]; last_tx.num_nonrct_inputs = sum_data[3];
last_tx.fee_str = xmreg::xmr_amount_to_str(_tx_info.fee, "{:0.3f}", false); last_tx.fee_str = xmreg::xmr_amount_to_str(_tx_info.fee, "{:0.3f}", false);
last_tx.payed_for_kB_str = fmt::format("{:0.4f}", payed_for_kB);
last_tx.xmr_inputs_str = xmreg::xmr_amount_to_str(last_tx.sum_inputs , "{:0.3f}"); last_tx.xmr_inputs_str = xmreg::xmr_amount_to_str(last_tx.sum_inputs , "{:0.3f}");
last_tx.xmr_outputs_str = xmreg::xmr_amount_to_str(last_tx.sum_outputs, "{:0.3f}"); last_tx.xmr_outputs_str = xmreg::xmr_amount_to_str(last_tx.sum_outputs, "{:0.3f}");
last_tx.timestamp_str = xmreg::timestamp_to_str_gm(_tx_info.receive_time); last_tx.timestamp_str = xmreg::timestamp_to_str_gm(_tx_info.receive_time);
last_tx.txsize = fmt::format("{:0.2f}", last_tx.txsize = fmt::format("{:0.2f}", tx_size);
static_cast<double>(_tx_info.blob_size)/1024.0);
} // if (hex_to_pod(_tx_info.id_hash, mem_tx_hash)) last_tx.pID = '-';
crypto::hash payment_id;
crypto::hash8 payment_id8;
get_payment_id(tx, payment_id, payment_id8);
if (payment_id != null_hash)
last_tx.pID = 'l'; // legacy payment id
else if (payment_id8 != null_hash8)
last_tx.pID = 'e'; // encrypted payment id
else if (!get_additional_tx_pub_keys_from_extra(tx).empty())
{
// if multioutput tx have additional public keys,
// mark it so that it represents that it has at least
// one sub-address
last_tx.pID = 's';
}
// } // if (hex_to_pod(_tx_info.id_hash, mem_tx_hash))
} // for (size_t i = 0; i < mempool_tx_info.size(); ++i) } // for (size_t i = 0; i < mempool_tx_info.size(); ++i)
@ -211,9 +223,7 @@ MempoolStatus::read_network_info()
COMMAND_RPC_GET_INFO::response rpc_network_info; COMMAND_RPC_GET_INFO::response rpc_network_info;
if (!rpc.get_network_info(rpc_network_info)) if (!rpc.get_network_info(rpc_network_info))
{
return false; return false;
}
uint64_t fee_estimated; uint64_t fee_estimated;
@ -229,6 +239,11 @@ MempoolStatus::read_network_info()
(void) error_msg; (void) error_msg;
COMMAND_RPC_HARD_FORK_INFO::response rpc_hardfork_info;
if (!rpc.get_hardfork_info(rpc_hardfork_info))
return false;
network_info local_copy; network_info local_copy;
@ -244,15 +259,19 @@ MempoolStatus::read_network_info()
local_copy.outgoing_connections_count = rpc_network_info.outgoing_connections_count; local_copy.outgoing_connections_count = rpc_network_info.outgoing_connections_count;
local_copy.incoming_connections_count = rpc_network_info.incoming_connections_count; local_copy.incoming_connections_count = rpc_network_info.incoming_connections_count;
local_copy.white_peerlist_size = rpc_network_info.white_peerlist_size; local_copy.white_peerlist_size = rpc_network_info.white_peerlist_size;
local_copy.testnet = rpc_network_info.testnet; local_copy.nettype = rpc_network_info.testnet ? cryptonote::network_type::TESTNET :
rpc_network_info.stagenet ? cryptonote::network_type::STAGENET : cryptonote::network_type::MAINNET;
local_copy.cumulative_difficulty = rpc_network_info.cumulative_difficulty; local_copy.cumulative_difficulty = rpc_network_info.cumulative_difficulty;
local_copy.block_size_limit = rpc_network_info.block_size_limit; local_copy.block_size_limit = rpc_network_info.block_size_limit;
local_copy.start_time = rpc_network_info.start_time; local_copy.start_time = rpc_network_info.start_time;
epee::string_tools::hex_to_pod(rpc_network_info.top_block_hash, local_copy.top_block_hash); epee::string_tools::hex_to_pod(rpc_network_info.top_block_hash, local_copy.top_block_hash);
local_copy.fee_per_kb = fee_estimated; local_copy.fee_per_kb = fee_estimated;
local_copy.info_timestamp = static_cast<uint64_t>(std::time(nullptr)); local_copy.info_timestamp = static_cast<uint64_t>(std::time(nullptr));
local_copy.current_hf_version = rpc_hardfork_info.version;
local_copy.current = true; local_copy.current = true;
current_network_info = local_copy; current_network_info = local_copy;
@ -285,7 +304,7 @@ MempoolStatus::is_thread_running()
bf::path MempoolStatus::blockchain_path {"/home/mwo/.bitmonero/lmdb"}; bf::path MempoolStatus::blockchain_path {"/home/mwo/.bitmonero/lmdb"};
string MempoolStatus::deamon_url {"http:://127.0.0.1:18081"}; string MempoolStatus::deamon_url {"http:://127.0.0.1:18081"};
bool MempoolStatus::testnet {false}; cryptonote::network_type MempoolStatus::nettype {cryptonote::network_type::MAINNET};
atomic<bool> MempoolStatus::is_running {false}; atomic<bool> MempoolStatus::is_running {false};
boost::thread MempoolStatus::m_thread; boost::thread MempoolStatus::m_thread;
Blockchain* MempoolStatus::core_storage {nullptr}; Blockchain* MempoolStatus::core_storage {nullptr};

@ -38,10 +38,15 @@ struct MempoolStatus
uint64_t mixin_no {0}; uint64_t mixin_no {0};
string fee_str; string fee_str;
string payed_for_kB_str;
string xmr_inputs_str; string xmr_inputs_str;
string xmr_outputs_str; string xmr_outputs_str;
string timestamp_str; string timestamp_str;
string txsize; string txsize;
char pID; // '-' - no payment ID,
// 'l' - legacy, long 64 character payment id,
// 'e' - encrypted, short, from integrated addresses
}; };
@ -62,11 +67,12 @@ struct MempoolStatus
uint64_t incoming_connections_count {0}; uint64_t incoming_connections_count {0};
uint64_t white_peerlist_size {0}; uint64_t white_peerlist_size {0};
uint64_t grey_peerlist_size {0}; uint64_t grey_peerlist_size {0};
bool testnet {false}; cryptonote::network_type nettype {cryptonote::network_type::MAINNET};
crypto::hash top_block_hash; crypto::hash top_block_hash;
uint64_t cumulative_difficulty {0}; uint64_t cumulative_difficulty {0};
uint64_t block_size_limit {0}; uint64_t block_size_limit {0};
uint64_t start_time {0}; uint64_t start_time {0};
uint64_t current_hf_version {0};
uint64_t hash_rate {0}; uint64_t hash_rate {0};
uint64_t fee_per_kb {0}; uint64_t fee_per_kb {0};
@ -114,7 +120,7 @@ struct MempoolStatus
static bf::path blockchain_path; static bf::path blockchain_path;
static string deamon_url; static string deamon_url;
static bool testnet; static cryptonote::network_type nettype;
// make object for accessing the blockchain here // make object for accessing the blockchain here
static MicroCore* mcore; static MicroCore* mcore;

@ -32,7 +32,9 @@ namespace xmreg
MicroCore::MicroCore(): MicroCore::MicroCore():
m_mempool(m_blockchain_storage), m_mempool(m_blockchain_storage),
m_blockchain_storage(m_mempool) m_blockchain_storage(m_mempool)
{} {
m_device = &hw::get_device("default");
}
/** /**
@ -88,7 +90,7 @@ MicroCore::init(const string& _blockchain_path)
// initialize Blockchain object to manage // initialize Blockchain object to manage
// the database. // the database.
return m_blockchain_storage.init(db, m_hardfork, false); return m_blockchain_storage.init(db, m_hardfork, network_type::MAINNET);
} }
/** /**
@ -358,4 +360,10 @@ MicroCore::get_blkchain_path()
return blockchain_path; return blockchain_path;
} }
hw::device* const
MicroCore::get_device() const
{
return m_device;
}
} }

@ -31,6 +31,8 @@ namespace xmreg
tx_memory_pool m_mempool; tx_memory_pool m_mempool;
Blockchain m_blockchain_storage; Blockchain m_blockchain_storage;
hw::device* m_device;
public: public:
MicroCore(); MicroCore();
@ -67,6 +69,8 @@ namespace xmreg
string string
get_blkchain_path(); get_blkchain_path();
hw::device* const
get_device() const;
virtual ~MicroCore(); virtual ~MicroCore();
}; };

@ -16,15 +16,15 @@
#define FEE_ESTIMATE_GRACE_BLOCKS 10 // estimate fee valid for that many blocks #define FEE_ESTIMATE_GRACE_BLOCKS 10 // estimate fee valid for that many blocks
#include "release/version/version.h" #include "version.h"
#include "net/http_client.h" #include "net/http_client.h"
#include "storages/http_abstract_invoke.h" #include "storages/http_abstract_invoke.h"
//#include "cryptonote_core/cryptonote_basic.h"
#include "cryptonote_core/tx_pool.h" #include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/blockchain.h" #include "cryptonote_core/blockchain.h"
#include "blockchain_db/lmdb/db_lmdb.h" #include "blockchain_db/lmdb/db_lmdb.h"
#include "device/device_default.hpp"
#include "wallet/wallet2.h" #include "wallet/wallet2.h"

File diff suppressed because it is too large Load Diff

@ -207,6 +207,68 @@ rpccalls::get_network_info(COMMAND_RPC_GET_INFO::response& response)
} }
bool
rpccalls::get_hardfork_info(COMMAND_RPC_HARD_FORK_INFO::response& response)
{
epee::json_rpc::request<cryptonote::COMMAND_RPC_HARD_FORK_INFO::request> req_t = AUTO_VAL_INIT(req_t);
epee::json_rpc::response<cryptonote::COMMAND_RPC_HARD_FORK_INFO::response, std::string> resp_t = AUTO_VAL_INIT(resp_t);
bool r {false};
req_t.jsonrpc = "2.0";
req_t.id = epee::serialization::storage_entry(0);
req_t.method = "hard_fork_info";
{
std::lock_guard<std::mutex> guard(m_daemon_rpc_mutex);
if (!connect_to_monero_deamon())
{
cerr << "get_hardfork_info: not connected to deamon" << endl;
return false;
}
r = epee::net_utils::invoke_http_json("/json_rpc",
req_t, resp_t,
m_http_client);
}
string err;
if (r)
{
if (resp_t.result.status == CORE_RPC_STATUS_BUSY)
{
err = "daemon is busy. Please try again later.";
}
else if (resp_t.result.status != CORE_RPC_STATUS_OK)
{
err = resp_t.result.status;
}
if (!err.empty())
{
cerr << "Error connecting to Monero deamon due to "
<< err << endl;
return false;
}
}
else
{
cerr << "Error connecting to Monero deamon at "
<< deamon_url << endl;
return false;
}
response = resp_t.result;
return true;
}
bool bool
rpccalls::get_dynamic_per_kb_fee_estimate( rpccalls::get_dynamic_per_kb_fee_estimate(
uint64_t grace_blocks, uint64_t grace_blocks,

@ -9,6 +9,7 @@
#include "monero_headers.h" #include "monero_headers.h"
#include <mutex> #include <mutex>
#include <utility>
@ -23,7 +24,7 @@ struct has_destructor
{ {
// has destructor // has destructor
template <typename A> template <typename A>
static std::true_type test(decltype(declval<A>().~A()) *) static std::true_type test(decltype(std::declval<A>().~A()) *)
{ {
return std::true_type(); return std::true_type();
} }
@ -98,6 +99,9 @@ public:
bool bool
get_network_info(COMMAND_RPC_GET_INFO::response& info); get_network_info(COMMAND_RPC_GET_INFO::response& info);
bool
get_hardfork_info( COMMAND_RPC_HARD_FORK_INFO::response& res);
bool bool
get_dynamic_per_kb_fee_estimate( get_dynamic_per_kb_fee_estimate(
uint64_t grace_blocks, uint64_t grace_blocks,

@ -5,11 +5,14 @@
<br/> <br/>
{{#testnet}} {{#testnet}}
<H4 style="margin:5px">Testnet address: Yes</H4> <H4 style="margin:5px">Network type: Testnet</H4>
{{/testnet}}
{{^testnet}}
<H4 style="margin:5px">Testnet address: No</H4>
{{/testnet}} {{/testnet}}
{{#stagenet}}
<H4 style="margin:5px">Network type: Stagenet</H4>
{{/stagenet}}
{{^testnet}}{{^stagenet}}
<H4 style="margin:5px">Network type: Mainnet</H4>
{{/stagenet}}{{/testnet}}
<br/> <br/>
<H4 style="margin:5px">Associated public keys</H4> <H4 style="margin:5px">Associated public keys</H4>

@ -12,13 +12,15 @@
{{#css_styles}}{{/css_styles}} {{#css_styles}}{{/css_styles}}
</style> </style>
{{#js_files}}{{/js_files}}
</head> </head>
<body> <body>
<div> <div>
<div class="center"> <div class="center">
<h1 class="center"><a href="/">Onion Monero Blockchain Explorer</a></h1> <h1 class="center"><a href="/">Onion Monero Blockchain Explorer</a></h1>
<h4 style="font-size: 15px; margin: 0px">(no javascript - no cookies - no web analytics trackers - no images - open sourced)</h4> <h4 style="font-size: 15px; margin: 0px">({{^enable_js}}no javascript - {{/enable_js}}no cookies - no web analytics trackers - no images - open sourced)</h4>
</div> </div>

@ -24,12 +24,18 @@
{{#testnet_url}} {{#testnet_url}}
| <a href="{{testnet_url}}">Go to testnet explorer</a> | <a href="{{testnet_url}}">Go to testnet explorer</a>
{{/testnet_url}} {{/testnet_url}}
{{#stagenet_url}}
| <a href="{{stagenet_url}}">Go to stagenet explorer</a>
{{/stagenet_url}}
{{#mainnet_url}} {{#mainnet_url}}
| <a href="{{mainnet_url}}">Go to mainnet explorer</a> | <a href="{{mainnet_url}}">Go to mainnet explorer</a>
{{/mainnet_url}} {{/mainnet_url}}
{{#testnet}} {{#testnet}}
| This is <span style="color:#ff6b62">testnet</span> blockchian | This is <span style="color:#ff6b62">testnet</span> blockchian
{{/testnet}} {{/testnet}}
{{#stagenet}}
| This is <span style="color:#ff6b62">stagenet</span> blockchian
{{/stagenet}}
</h3> </h3>
@ -37,6 +43,7 @@
{{#network_info}} {{#network_info}}
<h3 style="font-size: 12px; margin-top: 5px; margin-bottom: 3"> <h3 style="font-size: 12px; margin-top: 5px; margin-bottom: 3">
Network difficulty: {{difficulty}} Network difficulty: {{difficulty}}
| Hard fork: v{{current_hf_version}}
| Hash rate: {{hash_rate}} | Hash rate: {{hash_rate}}
| Fee per kb: {{fee_per_kb}} | Fee per kb: {{fee_per_kb}}
| Median block size limit: {{block_size_limit}} kB | Median block size limit: {{block_size_limit}} kB
@ -73,10 +80,10 @@
<td>height</td> <td>height</td>
<td>age {{age_format}}<!--(Δm)--></td> <td>age {{age_format}}<!--(Δm)--></td>
<td>size [kB]<!--(Δm)--></td> <td>size [kB]<!--(Δm)--></td>
<td>tx hash</td> <td>transaction hash</td>
<td>fees</td> <td>fee</td>
<td>outputs</td> <td>outputs</td>
<td>in(nonrct)/out</td> <td>in/out/pID</td>
<td>ring size</td> <td>ring size</td>
<td>tx size [kB]</td> <td>tx size [kB]</td>
</tr> </tr>
@ -88,7 +95,7 @@
<td><a href="/tx/{{hash}}">{{hash}}</a></td> <td><a href="/tx/{{hash}}">{{hash}}</a></td>
<td>{{tx_fee_short}}</td> <td>{{tx_fee_short}}</td>
<td>{{sum_outputs_short}}</td> <td>{{sum_outputs_short}}</td>
<td>{{no_inputs}}({{no_nonrct_inputs}})/{{no_outputs}}</td> <td>{{no_inputs}}/{{no_outputs}}/{{pID}}</td>
<td>{{mixin}}</td> <td>{{mixin}}</td>
<td>{{tx_size_short}}</td> <td>{{tx_size_short}}</td>
</tr> </tr>

@ -0,0 +1,190 @@
var cnBase58 = (function () {
var b58 = {};
var alphabet_str = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
var alphabet = [];
for (var i = 0; i < alphabet_str.length; i++) {
alphabet.push(alphabet_str.charCodeAt(i));
}
var encoded_block_sizes = [0, 2, 3, 5, 6, 7, 9, 10, 11];
var alphabet_size = alphabet.length;
var full_block_size = 8;
var full_encoded_block_size = 11;
var UINT64_MAX = new JSBigInt(2).pow(64);
function hextobin(hex) {
if (hex.length % 2 !== 0) throw "Hex string has invalid length!";
var res = new Uint8Array(hex.length / 2);
for (var i = 0; i < hex.length / 2; ++i) {
res[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
}
return res;
}
function bintohex(bin) {
var out = [];
for (var i = 0; i < bin.length; ++i) {
out.push(("0" + bin[i].toString(16)).slice(-2));
}
return out.join("");
}
function strtobin(str) {
var res = new Uint8Array(str.length);
for (var i = 0; i < str.length; i++) {
res[i] = str.charCodeAt(i);
}
return res;
}
function bintostr(bin) {
var out = [];
for (var i = 0; i < bin.length; i++) {
out.push(String.fromCharCode(bin[i]));
}
return out.join("");
}
function uint8_be_to_64(data) {
if (data.length < 1 || data.length > 8) {
throw "Invalid input length";
}
var res = JSBigInt.ZERO;
var twopow8 = new JSBigInt(2).pow(8);
var i = 0;
switch (9 - data.length) {
case 1:
res = res.add(data[i++]);
case 2:
res = res.multiply(twopow8).add(data[i++]);
case 3:
res = res.multiply(twopow8).add(data[i++]);
case 4:
res = res.multiply(twopow8).add(data[i++]);
case 5:
res = res.multiply(twopow8).add(data[i++]);
case 6:
res = res.multiply(twopow8).add(data[i++]);
case 7:
res = res.multiply(twopow8).add(data[i++]);
case 8:
res = res.multiply(twopow8).add(data[i++]);
break;
default:
throw "Impossible condition";
}
return res;
}
function uint64_to_8be(num, size) {
var res = new Uint8Array(size);
if (size < 1 || size > 8) {
throw "Invalid input length";
}
var twopow8 = new JSBigInt(2).pow(8);
for (var i = size - 1; i >= 0; i--) {
res[i] = num.remainder(twopow8).toJSValue();
num = num.divide(twopow8);
}
return res;
}
b58.encode_block = function (data, buf, index) {
if (data.length < 1 || data.length > full_encoded_block_size) {
throw "Invalid block length: " + data.length;
}
var num = uint8_be_to_64(data);
var i = encoded_block_sizes[data.length] - 1;
// while num > 0
while (num.compare(0) === 1) {
var div = num.divRem(alphabet_size);
// remainder = num % alphabet_size
var remainder = div[1];
// num = num / alphabet_size
num = div[0];
buf[index + i] = alphabet[remainder.toJSValue()];
i--;
}
return buf;
};
b58.encode = function (hex) {
var data = hextobin(hex);
if (data.length === 0) {
return "";
}
var full_block_count = Math.floor(data.length / full_block_size);
var last_block_size = data.length % full_block_size;
var res_size = full_block_count * full_encoded_block_size + encoded_block_sizes[last_block_size];
var res = new Uint8Array(res_size);
var i;
for (i = 0; i < res_size; ++i) {
res[i] = alphabet[0];
}
for (i = 0; i < full_block_count; i++) {
res = b58.encode_block(data.subarray(i * full_block_size, i * full_block_size + full_block_size), res, i * full_encoded_block_size);
}
if (last_block_size > 0) {
res = b58.encode_block(data.subarray(full_block_count * full_block_size, full_block_count * full_block_size + last_block_size), res, full_block_count * full_encoded_block_size)
}
return bintostr(res);
};
b58.decode_block = function (data, buf, index) {
if (data.length < 1 || data.length > full_encoded_block_size) {
throw "Invalid block length: " + data.length;
}
var res_size = encoded_block_sizes.indexOf(data.length);
if (res_size <= 0) {
throw "Invalid block size";
}
var res_num = new JSBigInt(0);
var order = new JSBigInt(1);
for (var i = data.length - 1; i >= 0; i--) {
var digit = alphabet.indexOf(data[i]);
if (digit < 0) {
throw "Invalid symbol";
}
var product = order.multiply(digit).add(res_num);
// if product > UINT64_MAX
if (product.compare(UINT64_MAX) === 1) {
throw "Overflow";
}
res_num = product;
order = order.multiply(alphabet_size);
}
if (res_size < full_block_size && (new JSBigInt(2).pow(8 * res_size).compare(res_num) <= 0)) {
throw "Overflow 2";
}
buf.set(uint64_to_8be(res_num, res_size), index);
return buf;
};
b58.decode = function (enc) {
enc = strtobin(enc);
if (enc.length === 0) {
return "";
}
var full_block_count = Math.floor(enc.length / full_encoded_block_size);
var last_block_size = enc.length % full_encoded_block_size;
var last_block_decoded_size = encoded_block_sizes.indexOf(last_block_size);
if (last_block_decoded_size < 0) {
throw "Invalid encoded length";
}
var data_size = full_block_count * full_block_size + last_block_decoded_size;
var data = new Uint8Array(data_size);
for (var i = 0; i < full_block_count; i++) {
data = b58.decode_block(enc.subarray(i * full_encoded_block_size, i * full_encoded_block_size + full_encoded_block_size), data, i * full_block_size);
}
if (last_block_size > 0) {
data = b58.decode_block(enc.subarray(full_block_count * full_encoded_block_size, full_block_count * full_encoded_block_size + last_block_size), data, full_block_count * full_block_size);
}
return bintohex(data);
};
return b58;
})();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,30 @@
var config = {
testnet: false, // this is adjusted page.h if needed. dont need to change manually
stagenet: false, // this is adjusted page.h if needed. dont need to change manually
coinUnitPlaces: 12,
txMinConfirms: 10, // corresponds to CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE in Monero
txCoinbaseMinConfirms: 60, // corresponds to CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW in Monero
coinSymbol: 'XMR',
openAliasPrefix: "xmr",
coinName: 'Monero',
coinUriPrefix: 'monero:',
addressPrefix: 18,
integratedAddressPrefix: 19,
subAddressPrefix: 42,
addressPrefixTestnet: 53,
integratedAddressPrefixTestnet: 54,
subAddressPrefixTestnet: 63,
addressPrefixStagenet: 24,
integratedAddressPrefixStagenet: 25,
subAddressPrefixStagenet: 36,
feePerKB: new JSBigInt('2000000000'),//20^10 - for testnet its not used, as fee is dynamic.
dustThreshold: new JSBigInt('1000000000'),//10^10 used for choosing outputs/change - we decompose all the way down if the receiver wants now regardless of threshold
txChargeRatio: 0.5,
defaultMixin: 4, // minimum mixin for hardfork v5
txChargeAddress: '',
idleTimeout: 30,
idleWarningDuration: 20,
maxBlockNumber: 500000000,
avgBlockTime: 120,
debugMode: false
};

@ -0,0 +1,65 @@
var crc32 = (function () {
'use strict';
var crc32 = {};
crc32.Utf8Encode = function (string) {
return unescape(encodeURIComponent(string));
};
crc32.run = function (str) {
var crc = new crc32.Type();
crc.processString(str);
return crc.checksum();
};
crc32.table = [
0, 1996959894, 3993919788, 2567524794, 124634137, 1886057615, 3915621685, 2657392035,
249268274, 2044508324, 3772115230, 2547177864, 162941995, 2125561021, 3887607047, 2428444049,
498536548, 1789927666, 4089016648, 2227061214, 450548861, 1843258603, 4107580753, 2211677639,
325883990, 1684777152, 4251122042, 2321926636, 335633487, 1661365465, 4195302755, 2366115317,
997073096, 1281953886, 3579855332, 2724688242, 1006888145, 1258607687, 3524101629, 2768942443,
901097722, 1119000684, 3686517206, 2898065728, 853044451, 1172266101, 3705015759, 2882616665,
651767980, 1373503546, 3369554304, 3218104598, 565507253, 1454621731, 3485111705, 3099436303,
671266974, 1594198024, 3322730930, 2970347812, 795835527, 1483230225, 3244367275, 3060149565,
1994146192, 31158534, 2563907772, 4023717930, 1907459465, 112637215, 2680153253, 3904427059,
2013776290, 251722036, 2517215374, 3775830040, 2137656763, 141376813, 2439277719, 3865271297,
1802195444, 476864866, 2238001368, 4066508878, 1812370925, 453092731, 2181625025, 4111451223,
1706088902, 314042704, 2344532202, 4240017532, 1658658271, 366619977, 2362670323, 4224994405,
1303535960, 984961486, 2747007092, 3569037538, 1256170817, 1037604311, 2765210733, 3554079995,
1131014506, 879679996, 2909243462, 3663771856, 1141124467, 855842277, 2852801631, 3708648649,
1342533948, 654459306, 3188396048, 3373015174, 1466479909, 544179635, 3110523913, 3462522015,
1591671054, 702138776, 2966460450, 3352799412, 1504918807, 783551873, 3082640443, 3233442989,
3988292384, 2596254646, 62317068, 1957810842, 3939845945, 2647816111, 81470997, 1943803523,
3814918930, 2489596804, 225274430, 2053790376, 3826175755, 2466906013, 167816743, 2097651377,
4027552580, 2265490386, 503444072, 1762050814, 4150417245, 2154129355, 426522225, 1852507879,
4275313526, 2312317920, 282753626, 1742555852, 4189708143, 2394877945, 397917763, 1622183637,
3604390888, 2714866558, 953729732, 1340076626, 3518719985, 2797360999, 1068828381, 1219638859,
3624741850, 2936675148, 906185462, 1090812512, 3747672003, 2825379669, 829329135, 1181335161,
3412177804, 3160834842, 628085408, 1382605366, 3423369109, 3138078467, 570562233, 1426400815,
3317316542, 2998733608, 733239954, 1555261956, 3268935591, 3050360625, 752459403, 1541320221,
2607071920, 3965973030, 1969922972, 40735498, 2617837225, 3943577151, 1913087877, 83908371,
2512341634, 3803740692, 2075208622, 213261112, 2463272603, 3855990285, 2094854071, 198958881,
2262029012, 4057260610, 1759359992, 534414190, 2176718541, 4139329115, 1873836001, 414664567,
2282248934, 4279200368, 1711684554, 285281116, 2405801727, 4167216745, 1634467795, 376229701,
2685067896, 3608007406, 1308918612, 956543938, 2808555105, 3495958263, 1231636301, 1047427035,
2932959818, 3654703836, 1088359270, 936918000, 2847714899, 3736837829, 1202900863, 817233897,
3183342108, 3401237130, 1404277552, 615818150, 3134207493, 3453421203, 1423857449, 601450431,
3009837614, 3294710456, 1567103746, 711928724, 3020668471, 3272380065, 1510334235, 755167117
];
crc32.Type = function () {
this.rem_ = 0xFFFFFFFF;
this.checksum = function () {
return ((this.rem_ ^ 0xFFFFFFFF) >>> 0);
};
this.processString = function (str) {
str = crc32.Utf8Encode(str);
for (var i = 0; i < str.length; i++) {
var byte_index = ((str.charCodeAt(i) ^ this.rem_) >>> 0) & 0xFF;
this.rem_ = ((this.rem_ >>> 8) ^ crc32.table[byte_index]) >>> 0;
}
};
return this;
};
return crc32;
})();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,571 @@
(function(nacl) {
'use strict';
// Ported in 2014 by Dmitry Chestnykh and Devi Mandiri.
// Public domain.
//
// Implementation derived from TweetNaCl version 20140427.
// See for details: http://tweetnacl.cr.yp.to/
// modified 2017 for some CN functions by luigi1111
var gf = function(init) {
var i, r = new Float64Array(16);
if (init) for (i = 0; i < init.length; i++) r[i] = init[i];
return r;
};
// Pluggable, initialized in high-level API below.
var randombytes = function(/* x, n */) { throw new Error('no PRNG'); };
var _0 = new Uint8Array(16);
var _9 = new Uint8Array(32); _9[0] = 9;
var gf0 = gf(),
gf1 = gf([1]),
_121665 = gf([0xdb41, 1]),
D = gf([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203]),
D2 = gf([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406]),
X = gf([0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169]),
Y = gf([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666]),
I = gf([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83]);
function vn(x, xi, y, yi, n) {
var i,d = 0;
for (i = 0; i < n; i++) d |= x[xi+i]^y[yi+i];
return (1 & ((d - 1) >>> 8)) - 1;
}
function crypto_verify_16(x, xi, y, yi) {
return vn(x,xi,y,yi,16);
}
function crypto_verify_32(x, xi, y, yi) {
return vn(x,xi,y,yi,32);
}
function set25519(r, a) {
var i;
for (i = 0; i < 16; i++) r[i] = a[i]|0;
}
function car25519(o) {
var i, v, c = 1;
for (i = 0; i < 16; i++) {
v = o[i] + c + 65535;
c = Math.floor(v / 65536);
o[i] = v - c * 65536;
}
o[0] += c-1 + 37 * (c-1);
}
function sel25519(p, q, b) {
var t, c = ~(b-1);
for (var i = 0; i < 16; i++) {
t = c & (p[i] ^ q[i]);
p[i] ^= t;
q[i] ^= t;
}
}
function pack25519(o, n) {
var i, j, b;
var m = gf(), t = gf();
for (i = 0; i < 16; i++) t[i] = n[i];
car25519(t);
car25519(t);
car25519(t);
for (j = 0; j < 2; j++) {
m[0] = t[0] - 0xffed;
for (i = 1; i < 15; i++) {
m[i] = t[i] - 0xffff - ((m[i-1]>>16) & 1);
m[i-1] &= 0xffff;
}
m[15] = t[15] - 0x7fff - ((m[14]>>16) & 1);
b = (m[15]>>16) & 1;
m[14] &= 0xffff;
sel25519(t, m, 1-b);
}
for (i = 0; i < 16; i++) {
o[2*i] = t[i] & 0xff;
o[2*i+1] = t[i]>>8;
}
}
function neq25519(a, b) {
var c = new Uint8Array(32), d = new Uint8Array(32);
pack25519(c, a);
pack25519(d, b);
return crypto_verify_32(c, 0, d, 0);
}
function par25519(a) {
var d = new Uint8Array(32);
pack25519(d, a);
return d[0] & 1;
}
function unpack25519(o, n) {
var i;
for (i = 0; i < 16; i++) o[i] = n[2*i] + (n[2*i+1] << 8);
o[15] &= 0x7fff;
}
function A(o, a, b) {
for (var i = 0; i < 16; i++) o[i] = a[i] + b[i];
}
function Z(o, a, b) {
for (var i = 0; i < 16; i++) o[i] = a[i] - b[i];
}
function M(o, a, b) {
var v, c,
t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0,
t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0,
t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0,
t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0,
b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5],
b6 = b[6], b7 = b[7], b8 = b[8], b9 = b[9], b10 = b[10],
b11 = b[11], b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15];
v = a[0];
t0 += v * b0; t1 += v * b1; t2 += v * b2; t3 += v * b3;
t4 += v * b4; t5 += v * b5; t6 += v * b6; t7 += v * b7;
t8 += v * b8; t9 += v * b9; t10 += v * b10; t11 += v * b11;
t12 += v * b12; t13 += v * b13; t14 += v * b14; t15 += v * b15;
v = a[1];
t1 += v * b0; t2 += v * b1; t3 += v * b2; t4 += v * b3;
t5 += v * b4; t6 += v * b5; t7 += v * b6; t8 += v * b7;
t9 += v * b8; t10 += v * b9; t11 += v * b10; t12 += v * b11;
t13 += v * b12; t14 += v * b13; t15 += v * b14; t16 += v * b15;
v = a[2];
t2 += v * b0; t3 += v * b1; t4 += v * b2; t5 += v * b3;
t6 += v * b4; t7 += v * b5; t8 += v * b6; t9 += v * b7;
t10 += v * b8; t11 += v * b9; t12 += v * b10; t13 += v * b11;
t14 += v * b12; t15 += v * b13; t16 += v * b14; t17 += v * b15;
v = a[3];
t3 += v * b0; t4 += v * b1; t5 += v * b2; t6 += v * b3;
t7 += v * b4; t8 += v * b5; t9 += v * b6; t10 += v * b7;
t11 += v * b8; t12 += v * b9; t13 += v * b10; t14 += v * b11;
t15 += v * b12; t16 += v * b13; t17 += v * b14; t18 += v * b15;
v = a[4];
t4 += v * b0; t5 += v * b1; t6 += v * b2; t7 += v * b3;
t8 += v * b4; t9 += v * b5; t10 += v * b6; t11 += v * b7;
t12 += v * b8; t13 += v * b9; t14 += v * b10; t15 += v * b11;
t16 += v * b12; t17 += v * b13; t18 += v * b14; t19 += v * b15;
v = a[5];
t5 += v * b0; t6 += v * b1; t7 += v * b2; t8 += v * b3;
t9 += v * b4; t10 += v * b5; t11 += v * b6; t12 += v * b7;
t13 += v * b8; t14 += v * b9; t15 += v * b10; t16 += v * b11;
t17 += v * b12; t18 += v * b13; t19 += v * b14; t20 += v * b15;
v = a[6];
t6 += v * b0; t7 += v * b1; t8 += v * b2; t9 += v * b3;
t10 += v * b4; t11 += v * b5; t12 += v * b6; t13 += v * b7;
t14 += v * b8; t15 += v * b9; t16 += v * b10; t17 += v * b11;
t18 += v * b12; t19 += v * b13; t20 += v * b14; t21 += v * b15;
v = a[7];
t7 += v * b0; t8 += v * b1; t9 += v * b2; t10 += v * b3;
t11 += v * b4; t12 += v * b5; t13 += v * b6; t14 += v * b7;
t15 += v * b8; t16 += v * b9; t17 += v * b10; t18 += v * b11;
t19 += v * b12; t20 += v * b13; t21 += v * b14; t22 += v * b15;
v = a[8];
t8 += v * b0; t9 += v * b1; t10 += v * b2; t11 += v * b3;
t12 += v * b4; t13 += v * b5; t14 += v * b6; t15 += v * b7;
t16 += v * b8; t17 += v * b9; t18 += v * b10; t19 += v * b11;
t20 += v * b12; t21 += v * b13; t22 += v * b14; t23 += v * b15;
v = a[9];
t9 += v * b0; t10 += v * b1; t11 += v * b2; t12 += v * b3;
t13 += v * b4; t14 += v * b5; t15 += v * b6; t16 += v * b7;
t17 += v * b8; t18 += v * b9; t19 += v * b10; t20 += v * b11;
t21 += v * b12; t22 += v * b13; t23 += v * b14; t24 += v * b15;
v = a[10];
t10 += v * b0; t11 += v * b1; t12 += v * b2; t13 += v * b3;
t14 += v * b4; t15 += v * b5; t16 += v * b6; t17 += v * b7;
t18 += v * b8; t19 += v * b9; t20 += v * b10; t21 += v * b11;
t22 += v * b12; t23 += v * b13; t24 += v * b14; t25 += v * b15;
v = a[11];
t11 += v * b0; t12 += v * b1; t13 += v * b2; t14 += v * b3;
t15 += v * b4; t16 += v * b5; t17 += v * b6; t18 += v * b7;
t19 += v * b8; t20 += v * b9; t21 += v * b10; t22 += v * b11;
t23 += v * b12; t24 += v * b13; t25 += v * b14; t26 += v * b15;
v = a[12];
t12 += v * b0; t13 += v * b1; t14 += v * b2; t15 += v * b3;
t16 += v * b4; t17 += v * b5; t18 += v * b6; t19 += v * b7;
t20 += v * b8; t21 += v * b9; t22 += v * b10; t23 += v * b11;
t24 += v * b12; t25 += v * b13; t26 += v * b14; t27 += v * b15;
v = a[13];
t13 += v * b0; t14 += v * b1; t15 += v * b2; t16 += v * b3;
t17 += v * b4; t18 += v * b5; t19 += v * b6; t20 += v * b7;
t21 += v * b8; t22 += v * b9; t23 += v * b10; t24 += v * b11;
t25 += v * b12; t26 += v * b13; t27 += v * b14; t28 += v * b15;
v = a[14];
t14 += v * b0; t15 += v * b1; t16 += v * b2; t17 += v * b3;
t18 += v * b4; t19 += v * b5; t20 += v * b6; t21 += v * b7;
t22 += v * b8; t23 += v * b9; t24 += v * b10; t25 += v * b11;
t26 += v * b12; t27 += v * b13; t28 += v * b14; t29 += v * b15;
v = a[15];
t15 += v * b0; t16 += v * b1; t17 += v * b2; t18 += v * b3;
t19 += v * b4; t20 += v * b5; t21 += v * b6; t22 += v * b7;
t23 += v * b8; t24 += v * b9; t25 += v * b10; t26 += v * b11;
t27 += v * b12; t28 += v * b13; t29 += v * b14; t30 += v * b15;
t0 += 38 * t16; t1 += 38 * t17; t2 += 38 * t18; t3 += 38 * t19;
t4 += 38 * t20; t5 += 38 * t21; t6 += 38 * t22; t7 += 38 * t23;
t8 += 38 * t24; t9 += 38 * t25; t10 += 38 * t26; t11 += 38 * t27;
t12 += 38 * t28; t13 += 38 * t29; t14 += 38 * t30; // t15 left as is
// first car
c = 1;
v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536;
v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536;
v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536;
v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536;
v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536;
v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536;
v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536;
v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536;
v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536;
v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536;
v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536;
v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536;
v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536;
v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536;
v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536;
v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536;
t0 += c-1 + 37 * (c-1);
// second car
c = 1;
v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536;
v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536;
v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536;
v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536;
v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536;
v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536;
v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536;
v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536;
v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536;
v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536;
v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536;
v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536;
v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536;
v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536;
v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536;
v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536;
t0 += c-1 + 37 * (c-1);
o[ 0] = t0;
o[ 1] = t1;
o[ 2] = t2;
o[ 3] = t3;
o[ 4] = t4;
o[ 5] = t5;
o[ 6] = t6;
o[ 7] = t7;
o[ 8] = t8;
o[ 9] = t9;
o[10] = t10;
o[11] = t11;
o[12] = t12;
o[13] = t13;
o[14] = t14;
o[15] = t15;
}
function S(o, a) {
M(o, a, a);
}
function inv25519(o, i) {
var c = gf();
var a;
for (a = 0; a < 16; a++) c[a] = i[a];
for (a = 253; a >= 0; a--) {
S(c, c);
if(a !== 2 && a !== 4) M(c, c, i);
}
for (a = 0; a < 16; a++) o[a] = c[a];
}
function pow2523(o, i) {
var c = gf();
var a;
for (a = 0; a < 16; a++) c[a] = i[a];
for (a = 250; a >= 0; a--) {
S(c, c);
if(a !== 1) M(c, c, i);
}
for (a = 0; a < 16; a++) o[a] = c[a];
}
function add(p, q) {
var a = gf(), b = gf(), c = gf(),
d = gf(), e = gf(), f = gf(),
g = gf(), h = gf(), t = gf();
Z(a, p[1], p[0]);
Z(t, q[1], q[0]);
M(a, a, t);
A(b, p[0], p[1]);
A(t, q[0], q[1]);
M(b, b, t);
M(c, p[3], q[3]);
M(c, c, D2);
M(d, p[2], q[2]);
A(d, d, d);
Z(e, b, a);
Z(f, d, c);
A(g, d, c);
A(h, b, a);
M(p[0], e, f);
M(p[1], h, g);
M(p[2], g, f);
M(p[3], e, h);
}
function cswap(p, q, b) {
var i;
for (i = 0; i < 4; i++) {
sel25519(p[i], q[i], b);
}
}
function pack(r, p) {
var tx = gf(), ty = gf(), zi = gf();
inv25519(zi, p[2]);
M(tx, p[0], zi);
M(ty, p[1], zi);
pack25519(r, ty);
r[31] ^= par25519(tx) << 7;
}
function scalarmult(p, q, s) {
var b, i;
set25519(p[0], gf0);
set25519(p[1], gf1);
set25519(p[2], gf1);
set25519(p[3], gf0);
for (i = 255; i >= 0; --i) {
b = (s[(i/8)|0] >> (i&7)) & 1;
cswap(p, q, b);
add(q, p);
add(p, p);
cswap(p, q, b);
}
}
function scalarbase(p, s) {
var q = [gf(), gf(), gf(), gf()];
set25519(q[0], X);
set25519(q[1], Y);
set25519(q[2], gf1);
M(q[3], X, Y);
scalarmult(p, q, s);
}
//new functions for CN - scalar operations are handled externally
// this only handles curve operations, except for Hp()
//why do we negate points when unpacking them???
function ge_neg(pub) {
pub[31] ^= 0x80;
}
//res = s*G
function ge_scalarmult_base(s) {
var p = [gf(), gf(), gf(), gf()];
scalarbase(p, s);
var pk = new Uint8Array(32);
pack(pk, p);
return pk;
}
//res = s*P
function ge_scalarmult(P, s) {
var p = [gf(), gf(), gf(), gf()],
upk = [gf(), gf(), gf(), gf()],
res = new Uint8Array(32);
ge_neg(P);
if (unpackneg(upk, P) !== 0) throw "non-0 error on point decode";
scalarmult(p, upk, s);
pack(res, p);
return res;
}
//res = c*P + r*G
function ge_double_scalarmult_base_vartime(c, P, r) {
var uP = [gf(), gf(), gf(), gf()],
cP = [gf(), gf(), gf(), gf()],
rG = [gf(), gf(), gf(), gf()],
res = new Uint8Array(32);
ge_neg(P);
if (unpackneg(uP, P) !== 0) throw "non-0 error on point decode";
scalarmult(cP, uP, c);
scalarbase(rG, r);
add(rG, cP);
pack(res, rG);
return res;
}
//name changed to reflect not using precomp; res = r*Pb + c*I
function ge_double_scalarmult_postcomp_vartime(r, Pb, c, I) {
var uPb = [gf(), gf(), gf(), gf()],
uI = [gf(), gf(), gf(), gf()],
cI = [gf(), gf(), gf(), gf()],
rPb = [gf(), gf(), gf(), gf()],
res = new Uint8Array(32);
ge_neg(Pb);
if (unpackneg(uPb, Pb) !== 0) throw "non-0 error on point decode";
scalarmult(rPb, uPb, r);
ge_neg(I);
if (unpackneg(uI, I) !== 0) throw "non-0 error on point decode";
scalarmult(cI, uI, c);
add(rPb, cI);
pack(res, rPb);
return res;
}
//res = P + Q
function ge_add(P, Q) {
var uP = [gf(), gf(), gf(), gf()],
uQ = [gf(), gf(), gf(), gf()],
res = new Uint8Array(32);
ge_neg(P);
ge_neg(Q);
if (unpackneg(uP, P) !== 0) throw "non-0 error on point decode";
if (unpackneg(uQ, Q) !== 0) throw "non-0 error on point decode";
add(uP, uQ);
pack(res, uP);
return res;
}
var L = new Float64Array([0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10]);
function modL(r, x) {
var carry, i, j, k;
for (i = 63; i >= 32; --i) {
carry = 0;
for (j = i - 32, k = i - 12; j < k; ++j) {
x[j] += carry - 16 * x[i] * L[j - (i - 32)];
carry = (x[j] + 128) >> 8;
x[j] -= carry * 256;
}
x[j] += carry;
x[i] = 0;
}
carry = 0;
for (j = 0; j < 32; j++) {
x[j] += carry - (x[31] >> 4) * L[j];
carry = x[j] >> 8;
x[j] &= 255;
}
for (j = 0; j < 32; j++) x[j] -= carry * L[j];
for (i = 0; i < 32; i++) {
x[i+1] += x[i] >> 8;
r[i] = x[i] & 255;
}
}
function reduce(r) {
var x = new Float64Array(64), i;
for (i = 0; i < 64; i++) x[i] = r[i];
for (i = 0; i < 64; i++) r[i] = 0;
modL(r, x);
}
function unpackneg(r, p) {
var t = gf(), chk = gf(), num = gf(),
den = gf(), den2 = gf(), den4 = gf(),
den6 = gf();
set25519(r[2], gf1);
unpack25519(r[1], p);
S(num, r[1]);
M(den, num, D);
Z(num, num, r[2]);
A(den, r[2], den);
S(den2, den);
S(den4, den2);
M(den6, den4, den2);
M(t, den6, num);
M(t, t, den);
pow2523(t, t);
M(t, t, num);
M(t, t, den);
M(t, t, den);
M(r[0], t, den);
S(chk, r[0]);
M(chk, chk, den);
if (neq25519(chk, num)) M(r[0], r[0], I);
S(chk, r[0]);
M(chk, chk, den);
if (neq25519(chk, num)) return -1;
if (par25519(r[0]) === (p[31]>>7)) Z(r[0], gf0, r[0]);
M(r[3], r[0], r[1]);
return 0;
}
nacl.ll = {
ge_scalarmult_base: ge_scalarmult_base,
ge_scalarmult: ge_scalarmult,
ge_double_scalarmult_base_vartime: ge_double_scalarmult_base_vartime,
ge_add: ge_add,
ge_double_scalarmult_postcomp_vartime: ge_double_scalarmult_postcomp_vartime
};
/* High-level API */
function cleanup(arr) {
for (var i = 0; i < arr.length; i++) arr[i] = 0;
}
nacl.randomBytes = function(n) {
var b = new Uint8Array(n);
randombytes(b, n);
return b;
};
nacl.setPRNG = function(fn) {
randombytes = fn;
};
(function() {
// Initialize PRNG if environment provides CSPRNG.
// If not, methods calling randombytes will throw.
var crypto = typeof self !== 'undefined' ? (self.crypto || self.msCrypto) : null;
if (crypto && crypto.getRandomValues) {
// Browsers.
var QUOTA = 65536;
nacl.setPRNG(function(x, n) {
var i, v = new Uint8Array(n);
for (i = 0; i < n; i += QUOTA) {
crypto.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA)));
}
for (i = 0; i < n; i++) x[i] = v[i];
cleanup(v);
});
} else if (typeof require !== 'undefined') {
// Node.js.
crypto = require('crypto');
if (crypto && crypto.randomBytes) {
nacl.setPRNG(function(x, n) {
var i, v = crypto.randomBytes(n);
for (i = 0; i < n; i++) x[i] = v[i];
cleanup(v);
});
}
}
})();
})(typeof module !== 'undefined' && module.exports ? module.exports : (self.nacl = self.nacl || {}));

@ -0,0 +1,473 @@
/*
* js-sha3 v0.5.1
* https://github.com/emn178/js-sha3
*
* Copyright 2015, emn178@gmail.com
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*/
;(function(root, undefined) {
'use strict';
var NODE_JS = typeof(module) != 'undefined';
if(NODE_JS) {
root = global;
if(root.JS_SHA3_TEST) {
root.navigator = { userAgent: 'Chrome'};
}
}
var HEX_CHARS = '0123456789abcdef'.split('');
var SHAKE_PADDING = [31, 7936, 2031616, 520093696];
var KECCAK_PADDING = [1, 256, 65536, 16777216];
var PADDING = [6, 1536, 393216, 100663296];
var SHIFT = [0, 8, 16, 24];
var RC = [1, 0, 32898, 0, 32906, 2147483648, 2147516416, 2147483648, 32907, 0, 2147483649,
0, 2147516545, 2147483648, 32777, 2147483648, 138, 0, 136, 0, 2147516425, 0,
2147483658, 0, 2147516555, 0, 139, 2147483648, 32905, 2147483648, 32771,
2147483648, 32770, 2147483648, 128, 2147483648, 32778, 0, 2147483658, 2147483648,
2147516545, 2147483648, 32896, 2147483648, 2147483649, 0, 2147516424, 2147483648];
var BITS = [224, 256, 384, 512];
var SHAKE_BITS = [128, 256];
var OUTPUT_TYPES = ['hex', 'buffer', 'array'];
var createOutputMethod = function(bits, padding, outputType) {
return function(message) {
return new Keccak(bits, padding, bits).update(message)[outputType]();
}
};
var createShakeOutputMethod = function(bits, padding, outputType) {
return function(message, outputBits) {
return new Keccak(bits, padding, outputBits).update(message)[outputType]();
}
};
var createMethod = function(bits, padding) {
var method = createOutputMethod(bits, padding, 'hex');
method.create = function() {
return new Keccak(bits, padding, bits);
};
method.update = function(message) {
return method.create().update(message);
};
for(var i = 0;i < OUTPUT_TYPES.length;++i) {
var type = OUTPUT_TYPES[i];
method[type] = createOutputMethod(bits, padding, type);
}
return method;
};
var createShakeMethod = function(bits, padding) {
var method = createShakeOutputMethod(bits, padding, 'hex');
method.create = function(outputBits) {
return new Keccak(bits, padding, outputBits);
};
method.update = function(message, outputBits) {
return method.create(outputBits).update(message);
};
for(var i = 0;i < OUTPUT_TYPES.length;++i) {
var type = OUTPUT_TYPES[i];
method[type] = createShakeOutputMethod(bits, padding, type);
}
return method;
};
var algorithms = [
{name: 'keccak', padding: KECCAK_PADDING, bits: BITS, createMethod: createMethod},
{name: 'sha3', padding: PADDING, bits: BITS, createMethod: createMethod},
{name: 'shake', padding: SHAKE_PADDING, bits: SHAKE_BITS, createMethod: createShakeMethod}
];
var methods = {};
for(var i = 0;i < algorithms.length;++i) {
var algorithm = algorithms[i];
var bits = algorithm.bits;
var createMethod = algorithm.createMethod;
for(var j = 0;j < bits.length;++j) {
var method = algorithm.createMethod(bits[j], algorithm.padding);
methods[algorithm.name +'_' + bits[j]] = method;
}
}
function Keccak(bits, padding, outputBits) {
this.blocks = [];
this.s = [];
this.padding = padding;
this.outputBits = outputBits;
this.reset = true;
this.block = 0;
this.start = 0;
this.blockCount = (1600 - (bits << 1)) >> 5;
this.byteCount = this.blockCount << 2;
this.outputBlocks = outputBits >> 5;
this.extraBytes = (outputBits & 31) >> 3;
for(var i = 0;i < 50;++i) {
this.s[i] = 0;
}
};
Keccak.prototype.update = function(message) {
var notString = typeof(message) != 'string';
if(notString && message.constructor == root.ArrayBuffer) {
message = new Uint8Array(message);
}
var length = message.length, blocks = this.blocks, byteCount = this.byteCount,
blockCount = this.blockCount, index = 0, s = this.s, i, code;
while(index < length) {
if(this.reset) {
this.reset = false;
blocks[0] = this.block;
for(i = 1;i < blockCount + 1;++i) {
blocks[i] = 0;
}
}
if(notString) {
for (i = this.start;index < length && i < byteCount; ++index) {
blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
}
} else {
for (i = this.start;index < length && i < byteCount; ++index) {
code = message.charCodeAt(index);
if (code < 0x80) {
blocks[i >> 2] |= code << SHIFT[i++ & 3];
} else if (code < 0x800) {
blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
} else if (code < 0xd800 || code >= 0xe000) {
blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
} else {
code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
}
}
}
this.lastByteIndex = i;
if(i >= byteCount) {
this.start = i - byteCount;
this.block = blocks[blockCount];
for(i = 0;i < blockCount;++i) {
s[i] ^= blocks[i];
}
f(s);
this.reset = true;
} else {
this.start = i;
}
}
return this;
};
Keccak.prototype.finalize = function() {
var blocks = this.blocks, i = this.lastByteIndex, blockCount = this.blockCount, s = this.s;
blocks[i >> 2] |= this.padding[i & 3];
if(this.lastByteIndex == this.byteCount) {
blocks[0] = blocks[blockCount];
for(i = 1;i < blockCount + 1;++i) {
blocks[i] = 0;
}
}
blocks[blockCount - 1] |= 0x80000000;
for(i = 0;i < blockCount;++i) {
s[i] ^= blocks[i];
}
f(s);
};
Keccak.prototype.toString = Keccak.prototype.hex = function() {
this.finalize();
var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
extraBytes = this.extraBytes, i = 0, j = 0;
var hex = '', block;
while(j < outputBlocks) {
for(i = 0;i < blockCount && j < outputBlocks;++i, ++j) {
block = s[i];
hex += HEX_CHARS[(block >> 4) & 0x0F] + HEX_CHARS[block & 0x0F] +
HEX_CHARS[(block >> 12) & 0x0F] + HEX_CHARS[(block >> 8) & 0x0F] +
HEX_CHARS[(block >> 20) & 0x0F] + HEX_CHARS[(block >> 16) & 0x0F] +
HEX_CHARS[(block >> 28) & 0x0F] + HEX_CHARS[(block >> 24) & 0x0F];
}
if(j % blockCount == 0) {
f(s);
}
}
if(extraBytes) {
block = s[i];
if(extraBytes > 0) {
hex += HEX_CHARS[(block >> 4) & 0x0F] + HEX_CHARS[block & 0x0F];
}
if(extraBytes > 1) {
hex += HEX_CHARS[(block >> 12) & 0x0F] + HEX_CHARS[(block >> 8) & 0x0F];
}
if(extraBytes > 2) {
hex += HEX_CHARS[(block >> 20) & 0x0F] + HEX_CHARS[(block >> 16) & 0x0F];
}
}
return hex;
};
Keccak.prototype.buffer = function() {
this.finalize();
var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
extraBytes = this.extraBytes, i = 0, j = 0;
var bytes = this.outputBits >> 3;
var buffer;
if(extraBytes) {
buffer = new ArrayBuffer((outputBlocks + 1) << 2);
} else {
buffer = new ArrayBuffer(bytes);
}
var array = new Uint32Array(buffer);
while(j < outputBlocks) {
for(i = 0;i < blockCount && j < outputBlocks;++i, ++j) {
array[j] = s[i];
}
if(j % blockCount == 0) {
f(s);
}
}
if(extraBytes) {
array[i] = s[i];
buffer = buffer.slice(0, bytes);
}
return buffer;
};
Keccak.prototype.digest = Keccak.prototype.array = function() {
this.finalize();
var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
extraBytes = this.extraBytes, i = 0, j = 0;
var array = [], offset, block;
while(j < outputBlocks) {
for(i = 0;i < blockCount && j < outputBlocks;++i, ++j) {
offset = j << 2;
block = s[i];
array[offset] = block & 0xFF;
array[offset + 1] = (block >> 8) & 0xFF;
array[offset + 2] = (block >> 16) & 0xFF;
array[offset + 3] = (block >> 24) & 0xFF;
}
if(j % blockCount == 0) {
f(s);
}
}
if(extraBytes) {
offset = j << 2;
block = s[i];
if(extraBytes > 0) {
array[offset] = block & 0xFF;
}
if(extraBytes > 1) {
array[offset + 1] = (block >> 8) & 0xFF;
}
if(extraBytes > 2) {
array[offset + 2] = (block >> 16) & 0xFF;
}
}
return array;
};
var f = function(s) {
var h, l, n, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9,
b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17,
b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33,
b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47, b48, b49;
for(n = 0; n < 48; n += 2) {
c0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40];
c1 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41];
c2 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42];
c3 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43];
c4 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44];
c5 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45];
c6 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46];
c7 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47];
c8 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48];
c9 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49];
h = c8 ^ ((c2 << 1) | (c3 >>> 31));
l = c9 ^ ((c3 << 1) | (c2 >>> 31));
s[0] ^= h;
s[1] ^= l;
s[10] ^= h;
s[11] ^= l;
s[20] ^= h;
s[21] ^= l;
s[30] ^= h;
s[31] ^= l;
s[40] ^= h;
s[41] ^= l;
h = c0 ^ ((c4 << 1) | (c5 >>> 31));
l = c1 ^ ((c5 << 1) | (c4 >>> 31));
s[2] ^= h;
s[3] ^= l;
s[12] ^= h;
s[13] ^= l;
s[22] ^= h;
s[23] ^= l;
s[32] ^= h;
s[33] ^= l;
s[42] ^= h;
s[43] ^= l;
h = c2 ^ ((c6 << 1) | (c7 >>> 31));
l = c3 ^ ((c7 << 1) | (c6 >>> 31));
s[4] ^= h;
s[5] ^= l;
s[14] ^= h;
s[15] ^= l;
s[24] ^= h;
s[25] ^= l;
s[34] ^= h;
s[35] ^= l;
s[44] ^= h;
s[45] ^= l;
h = c4 ^ ((c8 << 1) | (c9 >>> 31));
l = c5 ^ ((c9 << 1) | (c8 >>> 31));
s[6] ^= h;
s[7] ^= l;
s[16] ^= h;
s[17] ^= l;
s[26] ^= h;
s[27] ^= l;
s[36] ^= h;
s[37] ^= l;
s[46] ^= h;
s[47] ^= l;
h = c6 ^ ((c0 << 1) | (c1 >>> 31));
l = c7 ^ ((c1 << 1) | (c0 >>> 31));
s[8] ^= h;
s[9] ^= l;
s[18] ^= h;
s[19] ^= l;
s[28] ^= h;
s[29] ^= l;
s[38] ^= h;
s[39] ^= l;
s[48] ^= h;
s[49] ^= l;
b0 = s[0];
b1 = s[1];
b32 = (s[11] << 4) | (s[10] >>> 28);
b33 = (s[10] << 4) | (s[11] >>> 28);
b14 = (s[20] << 3) | (s[21] >>> 29);
b15 = (s[21] << 3) | (s[20] >>> 29);
b46 = (s[31] << 9) | (s[30] >>> 23);
b47 = (s[30] << 9) | (s[31] >>> 23);
b28 = (s[40] << 18) | (s[41] >>> 14);
b29 = (s[41] << 18) | (s[40] >>> 14);
b20 = (s[2] << 1) | (s[3] >>> 31);
b21 = (s[3] << 1) | (s[2] >>> 31);
b2 = (s[13] << 12) | (s[12] >>> 20);
b3 = (s[12] << 12) | (s[13] >>> 20);
b34 = (s[22] << 10) | (s[23] >>> 22);
b35 = (s[23] << 10) | (s[22] >>> 22);
b16 = (s[33] << 13) | (s[32] >>> 19);
b17 = (s[32] << 13) | (s[33] >>> 19);
b48 = (s[42] << 2) | (s[43] >>> 30);
b49 = (s[43] << 2) | (s[42] >>> 30);
b40 = (s[5] << 30) | (s[4] >>> 2);
b41 = (s[4] << 30) | (s[5] >>> 2);
b22 = (s[14] << 6) | (s[15] >>> 26);
b23 = (s[15] << 6) | (s[14] >>> 26);
b4 = (s[25] << 11) | (s[24] >>> 21);
b5 = (s[24] << 11) | (s[25] >>> 21);
b36 = (s[34] << 15) | (s[35] >>> 17);
b37 = (s[35] << 15) | (s[34] >>> 17);
b18 = (s[45] << 29) | (s[44] >>> 3);
b19 = (s[44] << 29) | (s[45] >>> 3);
b10 = (s[6] << 28) | (s[7] >>> 4);
b11 = (s[7] << 28) | (s[6] >>> 4);
b42 = (s[17] << 23) | (s[16] >>> 9);
b43 = (s[16] << 23) | (s[17] >>> 9);
b24 = (s[26] << 25) | (s[27] >>> 7);
b25 = (s[27] << 25) | (s[26] >>> 7);
b6 = (s[36] << 21) | (s[37] >>> 11);
b7 = (s[37] << 21) | (s[36] >>> 11);
b38 = (s[47] << 24) | (s[46] >>> 8);
b39 = (s[46] << 24) | (s[47] >>> 8);
b30 = (s[8] << 27) | (s[9] >>> 5);
b31 = (s[9] << 27) | (s[8] >>> 5);
b12 = (s[18] << 20) | (s[19] >>> 12);
b13 = (s[19] << 20) | (s[18] >>> 12);
b44 = (s[29] << 7) | (s[28] >>> 25);
b45 = (s[28] << 7) | (s[29] >>> 25);
b26 = (s[38] << 8) | (s[39] >>> 24);
b27 = (s[39] << 8) | (s[38] >>> 24);
b8 = (s[48] << 14) | (s[49] >>> 18);
b9 = (s[49] << 14) | (s[48] >>> 18);
s[0] = b0 ^ (~b2 & b4);
s[1] = b1 ^ (~b3 & b5);
s[10] = b10 ^ (~b12 & b14);
s[11] = b11 ^ (~b13 & b15);
s[20] = b20 ^ (~b22 & b24);
s[21] = b21 ^ (~b23 & b25);
s[30] = b30 ^ (~b32 & b34);
s[31] = b31 ^ (~b33 & b35);
s[40] = b40 ^ (~b42 & b44);
s[41] = b41 ^ (~b43 & b45);
s[2] = b2 ^ (~b4 & b6);
s[3] = b3 ^ (~b5 & b7);
s[12] = b12 ^ (~b14 & b16);
s[13] = b13 ^ (~b15 & b17);
s[22] = b22 ^ (~b24 & b26);
s[23] = b23 ^ (~b25 & b27);
s[32] = b32 ^ (~b34 & b36);
s[33] = b33 ^ (~b35 & b37);
s[42] = b42 ^ (~b44 & b46);
s[43] = b43 ^ (~b45 & b47);
s[4] = b4 ^ (~b6 & b8);
s[5] = b5 ^ (~b7 & b9);
s[14] = b14 ^ (~b16 & b18);
s[15] = b15 ^ (~b17 & b19);
s[24] = b24 ^ (~b26 & b28);
s[25] = b25 ^ (~b27 & b29);
s[34] = b34 ^ (~b36 & b38);
s[35] = b35 ^ (~b37 & b39);
s[44] = b44 ^ (~b46 & b48);
s[45] = b45 ^ (~b47 & b49);
s[6] = b6 ^ (~b8 & b0);
s[7] = b7 ^ (~b9 & b1);
s[16] = b16 ^ (~b18 & b10);
s[17] = b17 ^ (~b19 & b11);
s[26] = b26 ^ (~b28 & b20);
s[27] = b27 ^ (~b29 & b21);
s[36] = b36 ^ (~b38 & b30);
s[37] = b37 ^ (~b39 & b31);
s[46] = b46 ^ (~b48 & b40);
s[47] = b47 ^ (~b49 & b41);
s[8] = b8 ^ (~b0 & b2);
s[9] = b9 ^ (~b1 & b3);
s[18] = b18 ^ (~b10 & b12);
s[19] = b19 ^ (~b11 & b13);
s[28] = b28 ^ (~b20 & b22);
s[29] = b29 ^ (~b21 & b23);
s[38] = b38 ^ (~b30 & b32);
s[39] = b39 ^ (~b31 & b33);
s[48] = b48 ^ (~b40 & b42);
s[49] = b49 ^ (~b41 & b43);
s[0] ^= RC[n];
s[1] ^= RC[n + 1];
}
}
if(!root.JS_SHA3_TEST && NODE_JS) {
module.exports = methods;
} else if(root) {
for(var key in methods) {
root[key] = methods[key];
}
}
}(this));

@ -8,9 +8,9 @@
<tr> <tr>
<td>age [h:m:s]</td> <td>age [h:m:s]</td>
<td>transaction hash</td> <td>transaction hash</td>
<td>fee</td> <td>fee/per_kB</td>
<td>outputs</td> <!--<td>outputs</td>-->
<td>in(nonrct)/out</td> <td>in/out/pID</td>
<td>ring size</td> <td>ring size</td>
<td>tx size [kB]</td> <td>tx size [kB]</td>
</tr> </tr>
@ -18,9 +18,9 @@
<tr> <tr>
<td>{{age}}</td> <td>{{age}}</td>
<td><a href="/tx/{{hash}}">{{hash}}</a></td> <td><a href="/tx/{{hash}}">{{hash}}</a></td>
<td>{{fee}}</td> <td>{{fee}}/{{payed_for_kB}}</td>
<td>{{xmr_outputs}}</td> <!--<td>{{xmr_outputs}}</td>-->
<td>{{no_inputs}}({{no_nonrct_inputs}})/{{no_outputs}}</td> <td>{{no_inputs}}/{{no_outputs}}/{{pID}}</td>
<td>{{mixin}}</td> <td>{{mixin}}</td>
<td>{{txsize}}</td> <td>{{txsize}}</td>
</tr> </tr>

@ -14,7 +14,8 @@
<H5 style="margin:5px">Payment id (encrypted): {{payment_id8}}</H5> <H5 style="margin:5px">Payment id (encrypted): {{payment_id8}}</H5>
{{/decrypted_payment_id8}} {{/decrypted_payment_id8}}
{{#decrypted_payment_id8}} {{#decrypted_payment_id8}}
<H5 style="margin:5px">Payment id (decrypted): {{decrypted_payment_id8}}</H5> <H5 style="margin:5px">Payment id (decrypted): {{decrypted_payment_id8}}
(value incorrect if you are not the recipient of the tx)</H5>
{{/decrypted_payment_id8}} {{/decrypted_payment_id8}}
{{/has_payment_id8}} {{/has_payment_id8}}

@ -5,15 +5,16 @@
{{#enable_mixins_details}} {{#enable_mixins_details}}
<H5 style="margin:5px">Tx prefix hash: {{tx_prefix_hash}}</H5> <H5 style="margin:5px">Tx prefix hash: {{tx_prefix_hash}}</H5>
{{/enable_mixins_details}} {{/enable_mixins_details}}
<H5 style="margin:5px">Tx public key: {{tx_pub_key}}</H5> <H5 style="margin:5px">Tx public key: <span id="tx_pub_key">{{tx_pub_key}}</span></H5>
<span id="add_tx_pub_keys" style="display: none;">{{add_tx_pub_keys}}</span>
{{#has_payment_id}} {{#has_payment_id}}
<H5 style="margin:5px">Payment id: {{payment_id}}</H5> <H5 style="margin:5px">Payment id: <span id="payment_id">{{payment_id}}</span></H5>
<H5 style="margin:5px">Payment id as ascii ([a-zA-Z0-9 /!]): {{payment_id_as_ascii}}</H5> <H5 style="margin:5px">Payment id as ascii: {{payment_id_as_ascii}}</H5>
{{/has_payment_id}} {{/has_payment_id}}
{{#has_payment_id8}} {{#has_payment_id8}}
<H5 style="margin:5px">Payment id (encrypted): {{payment_id8}}</H5> <H5 style="margin:5px">Payment id (encrypted): <span id="payment_id">{{payment_id8}}</span></H5>
{{/has_payment_id8}} {{/has_payment_id8}}
@ -37,7 +38,7 @@
{{/have_raw_tx}} {{/have_raw_tx}}
<tr> <tr>
<td>Block: <a href="/block/{{blk_height}}">{{blk_height}}</a></td> <td>Block: <a href="/block/{{blk_height}}">{{blk_height}}</a></td>
<td>Fee: {{tx_fee}}</td> <td>Fee (per_kB): {{tx_fee}} ({{payed_for_kB}})</td>
<td>Tx size: {{tx_size}} kB</td> <td>Tx size: {{tx_size}} kB</td>
</tr> </tr>
<tr> <tr>
@ -48,7 +49,7 @@
<tr> <tr>
<td colspan="3">Extra: {{extra}}</td> <td colspan="3" style="max-width:700px">Extra: {{extra}}</td>
</tr> </tr>
@ -81,19 +82,32 @@
<input type="radio" id="tab-1" name="tab-group-1" checked> <input type="radio" id="tab-1" name="tab-group-1" checked>
<label for="tab-1">Decode outputs</label> <label for="tab-1">Decode outputs</label>
<div class="content"> <div class="content">
<h4 style="margin: 0px">Check which outputs belong to given Monero address and viewkey</h4> <h4 style="margin: 0px">Check which outputs belong to given Monero address/subaddress and viewkey</h4>
<h5 style="margin: 0px"> <h5 style="margin: 0px">
For RingCT transactions, outputs' amounts are also decoded For RingCT transactions, outputs' amounts are also decoded
<br/> <br/>
Note: address and viewkey are sent to the server, as the calculations are done on the server side {{#enable_js}}
Note: address/subaddress and viewkey are NOT sent to the server, as the calculations are done on the client side
{{/enable_js}}
{{^enable_js}}
Note: address/subaddress and viewkey are sent to the server, as the calculations are done on the server side
{{/enable_js}}
</h5> </h5>
<form action="/myoutputs" method="post" style="width:100%; margin-top:2px" class="style-1"> <form action="/myoutputs" method="post" style="width:100%; margin-top:2px" class="style-1">
<input type="hidden" name="tx_hash" value="{{tx_hash}}"><br/> <input type="hidden" name="tx_hash" value="{{tx_hash}}"><br/>
<input type="text" name="xmr_address" size="90" placeholder="Monero address"><br/> <input type="text" name="xmr_address" size="110" placeholder="Monero address/subaddress"><br/>
<input type="text" name="viewkey" size="90" placeholder="Private viewkey" style="margin-top:5px"><br/> <input type="text" name="viewkey" size="110" placeholder="Private viewkey" style="margin-top:5px"><br/>
<input type="hidden" name="raw_tx_data" value="{{raw_tx_data}}"> <input type="hidden" name="raw_tx_data" value="{{raw_tx_data}}">
<!--above raw_tx_data field only used when checking raw tx data through tx pusher--> <!--above raw_tx_data field only used when checking raw tx data through tx pusher-->
{{#enable_js}}
<!-- if have js, DONOT submit the form to server.
change submit button, to just a button -->
<button type="button" style="margin-top:5px" id="decode_btn" >Decode outputs</button>
{{/enable_js}}
{{^enable_js}}
<input type="submit" value="Decode outputs" style="margin-top:5px" > <input type="submit" value="Decode outputs" style="margin-top:5px" >
{{/enable_js}}
</form> </form>
</div> </div>
</div> </div>
@ -108,13 +122,29 @@
Tx private key can be obtained using <i>get_tx_key</i> Tx private key can be obtained using <i>get_tx_key</i>
command in <i>monero-wallet-cli</i> command line tool command in <i>monero-wallet-cli</i> command line tool
<br/> <br/>
Note: address and tx private key are sent to the server, as the calculations are done on the server side {{#enable_js}}
Note: address/subaddress and tx private key are NOT sent to the server, as the calculations are done on the client side
{{/enable_js}}
{{^enable_js}}
Note: address/subaddress and tx private key are sent to the server, as the calculations are done on the server side
{{/enable_js}}
</h5> </h5>
<form action="/prove" method="post" style="width:100%;margin-top:2px" class="style-1"> <form action="/prove" method="post" style="width:100%;margin-top:2px" class="style-1">
<input type="hidden" name="txhash" value="{{tx_hash}}"><br/> <input type="hidden" name="txhash" value="{{tx_hash}}"><br/>
<input type="text" name="txprvkey" size="90" placeholder="Tx private key"><br/> <input type="text" name="txprvkey" size="120" placeholder="Tx private key"><br/>
<input type="text" name="xmraddress" size="90" placeholder="Recipient's Monero Address" style="margin-top:5px"><br/> <input type="hidden" name="raw_tx_data" value="{{raw_tx_data}}">
<!--above raw_tx_data field only used when checking raw tx data through tx pusher-->
<input type="text" name="xmraddress" size="120" placeholder="Recipient's monero address/subaddress" style="margin-top:5px"><br/>
{{#enable_js}}
<!-- if have js, DONOT submit the form to server.
change submit button, to just a button -->
<button type="button" style="margin-top:5px" id="prove_btn">Prove sending</button>
{{/enable_js}}
{{^enable_js}}
<input type="submit" value="Prove sending" style="margin-top:5px"> <input type="submit" value="Prove sending" style="margin-top:5px">
{{/enable_js}}
</form> </form>
</div> </div>
</div> </div>
@ -122,6 +152,234 @@
</div> </div>
{{/have_raw_tx}} {{/have_raw_tx}}
{{#enable_js}}
<!-- to disply results from deconding and proving txs using js -->
<div id="decode-prove-results" class="center" style="width: 80%; margin-top:10px;border-style: dotted">
</div>
<script>
// here we handle button presses from the above forms
// to decode and prove txs.
$(document).ready(function() {
// we need output pubplic keys, their indexes and amounts.
// all this is already avaliable on the html, but we can use
// musch framework to produce js array for this
var tx_json = {{#tx_json_raw}}{{/tx_json_raw}};
var tx_public_key = $("#tx_pub_key").text();
// when we process multi-ouput tx, it can have extra public keys
// due to sub-addresses
var add_tx_pub_keys = $("#add_tx_pub_keys").text().split(';').slice(0, -1);
//console.log("add_tx_pub_keys: ", add_tx_pub_keys);
var payment_id = $("#payment_id").text();
$("#decode_btn").click(function() {
var address = $("input[name=xmr_address]").val();
var viewkey = $("input[name=viewkey]").val();
if (!address || !viewkey) {
$("#decode-prove-results").html("<h4>Address or viewkey key not provided!</h4>");
return;
}
// not used when decoding, but used when proving.
// so we just use array here
multiple_tx_secret_keys = [];
try {
var address_decoded = decode_address(address);
decodeOutputs(tx_json, tx_public_key, viewkey,
address_decoded.spend, payment_id,
add_tx_pub_keys, multiple_tx_secret_keys, false);
} catch(err){
console.log(err);
$("#decode-prove-results").html('<h4>Error: ' + err + '</h4>' );
}
});
$("#prove_btn").click(function() {
var address = $("input[name=xmraddress]").val();
var tx_prv_key = $("input[name=txprvkey]").val();
if (!address || !tx_prv_key) {
$("#decode-prove-results").html("<h4>Address or tx private key not provided!</h4>");
return;
}
try {
// when using subaddress, there can be more than one tx_prv_key
var multiple_tx_prv_keys = parse_str_secret_key(tx_prv_key);
var address_decoded = decode_address(address);
decodeOutputs(tx_json, address_decoded.view, tx_prv_key,
address_decoded.spend, payment_id,
add_tx_pub_keys, multiple_tx_prv_keys, true);
} catch(err){
console.log(err);
$("#decode-prove-results").html('<h4>Error: ' + err + '</h4>' );
}
});
});
// based on C++ code by stoffu
function parse_str_secret_key(key_str) {
var multiple_tx_secret_keys = [];
var num_keys = Math.floor(key_str.length / 64);
if (num_keys * 64 != key_str.length)
throw "num_keys * 64 != key_str.length";
for (var i = 0; i < num_keys; i++)
{
multiple_tx_secret_keys.push(key_str.slice(64*i, 64*i + 64));
}
return multiple_tx_secret_keys;
}
function decodeOutputs(tx_json, pub_key, sec_key,
address_pub_key, payment_id,
add_tx_pub_keys, multiple_tx_prv_keys, tx_prove) {
//console.log(tx_json);
var is_rct = (tx_json.version === 2);
var rct_type = (is_rct ? tx_json.rct_signatures.type : -1);
var key_derivation = "";
if (tx_prove)
key_derivation = generate_key_derivation(pub_key, multiple_tx_prv_keys[0]);
else
key_derivation = generate_key_derivation(pub_key, sec_key);
var add_key_derivation = [];
if (add_tx_pub_keys) {
for (var i = 0; i < add_tx_pub_keys.length; i++)
{
if (!tx_prove)
add_key_derivation.push(generate_key_derivation(add_tx_pub_keys[i], sec_key));
else
add_key_derivation.push(generate_key_derivation(pub_key, multiple_tx_prv_keys[i+1]));
}
}
//console.log("add_key_derivation: ", add_key_derivation);
// go over each tx output, and check if it is ours or not
var decoding_results_str = '<h3>Output decoding results</h3>';
decoding_results_str += '<table class="center">';
decoding_results_str += '<tr>' +
'<td></td>' +
'<td>output public key</td>' +
'<td>amount</td>' +
'<td>output match?</td>' +
'</tr>';
var output_idx = 0;
var sum_outptus = 0;
tx_json.vout.forEach(function(output) {
var output_pub_key = output.target.key;
var amount = output.amount;
var pubkey_generated = derive_public_key(key_derivation, output_idx, address_pub_key);
var mine_output = (output_pub_key == pubkey_generated);
var with_additional = false;
var mine_output_str = "false";
if (!mine_output && add_tx_pub_keys.length == tx_json.vout.length) {
pubkey_generated = derive_public_key(add_key_derivation[output_idx],
output_idx, address_pub_key);
mine_output = (output_pub_key == pubkey_generated);
with_additional = true;
}
if (mine_output) {
mine_output_str = '<span style="color: #008009;font-weight: bold">true</span>';
if (is_rct) {
try {
//var ecdh = decodeRct(tx_json.rct_signatures, output_idx, key_derivation);
var ecdh = decodeRct(tx_json.rct_signatures, output_idx,
(with_additional ? add_key_derivation[output_idx] : key_derivation));
amount = parseInt(ecdh.amount);
} catch (err) {
decoding_results_str += "<span class='validNo'>RingCT amount for output " + i + " with pubkey: " + output_pub_key + "</span>" + "<br>"; //rct commitment != computed
throw "invalid rct amount";
}
}
sum_outptus += amount;
}
decoding_results_str += "<tr>"
+"<td>" + output_idx + "</td>"
+"<td>" + output_pub_key + "</td>"
+"<td>" + (amount / 1e12) + "</td>"
+"<td>" + mine_output_str + "</td>"
+"</tr>";
//console.log(output[1], pubkey_generated);
output_idx++;
});
decoding_results_str += "</table>";
decoding_results_str += "<h3>Sum XMR from matched outputs (i.e., incoming XMR): " + (sum_outptus / 1e12) + "</h3>"
// decrypt payment_id8 which results in using
// integrated address
if (payment_id.length == 16) {
if (pub_key) {
var decrypted_payment_id8
= decrypt_payment_id(payment_id, pub_key, sec_key);
console.log("decrypted_payment_id8: " + decrypted_payment_id8);
decoding_results_str += "<h5>Decrypted payment id: "
+ decrypted_payment_id8
+ " (value incorrect if you are not the recipient of the tx)</h5>"
}
}
$("#decode-prove-results").html(decoding_results_str);
}
</script>
{{/enable_js}}
{{#has_inputs}} {{#has_inputs}}

@ -8,10 +8,14 @@
<div class="center"> <div class="center">
<form action="/checkandpush" method="post" style="width:100%; margin-top:15px" class="style-1"> <form action="/checkandpush" method="post" style="width:100%; margin-top:15px" class="style-1">
Paste here either a hex string of raw transaction (the <tt>tx_blob</tt> response in the wallet RPC, or the <tt>raw_monero_tx</tt> file saved by the wallet CLI with <tt>--do-not-relay</tt> option specified),<br/> Paste here either a hex string of raw transaction<br/>
(the <i>tx_blob</i> response in the wallet RPC, or the <i>raw_monero_tx</i>
file saved by the wallet CLI with <i>--do-not-relay</i> option specified),<br/>
or base64 encoded, unsigned or signed transaction data<br/> or base64 encoded, unsigned or signed transaction data<br/>
(In Linux, can get base64 signed raw tx data: <i>base64 signed_monero_tx | xclip -selection clipboard</i>)<br/> <br/>
(In Windows, can get base64 signed raw tx data: <i>certutil.exe -encode -f signed_monero_tx encoded.txt & type "encoded.txt" | clip</i>)<br/> (In Linux, can get the raw tx data: <i>cat raw_monero_tx | xclip -selection clipboard</i>)<br/>
(In Windows, can get the raw tx data: <i>certutil.exe -encode -f raw_monero_tx encoded.txt & type "encoded.txt" | clip</i>)<br/>
<textarea name="rawtxdata" rows="20" cols="80"></textarea> <textarea name="rawtxdata" rows="20" cols="80"></textarea>
<br/> <br/>
Note: data is sent to the server, as the calculations are done on the server side Note: data is sent to the server, as the calculations are done on the server side

File diff suppressed because it is too large Load Diff

@ -24,14 +24,14 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/algorithm/string.hpp>
#include <string> #include <string>
#include <vector> #include <vector>
#include <iterator> #include <iterator>
#include <algorithm> #include <algorithm>
#include <type_traits> #include <type_traits>
#include <regex>
/** /**
* Some helper functions used in the example. * Some helper functions used in the example.
@ -41,16 +41,16 @@
namespace xmreg namespace xmreg
{ {
using namespace cryptonote; using namespace cryptonote;
using namespace crypto; using namespace crypto;
using namespace std; using namespace std;
namespace bf = boost::filesystem; namespace bf = boost::filesystem;
using json = nlohmann::json; using json = nlohmann::json;
struct outputs_visitor struct outputs_visitor
{ {
std::vector<crypto::public_key >& m_output_keys; std::vector<crypto::public_key >& m_output_keys;
const Blockchain& m_bch; const Blockchain& m_bch;
@ -65,191 +65,218 @@ namespace xmreg
m_output_keys.push_back(pubkey); m_output_keys.push_back(pubkey);
return true; return true;
} }
}; };
template <typename T>
bool
parse_str_secret_key(const string& key_str, T& secret_key);
template <typename T>
bool
parse_str_secret_key(const string& key_str, std::vector<T>& secret_keys)
{
const size_t num_keys = key_str.size() / 64;
if (num_keys * 64 != key_str.size())
return false;
secret_keys.resize(num_keys);
for (size_t i = 0; i < num_keys; ++i)
{
if (!parse_str_secret_key(key_str.substr(64*i, 64), secret_keys[i]))
return false;
}
template <typename T> return true;
bool }
parse_str_secret_key(const string& key_str, T& secret_key);
bool bool
get_tx_pub_key_from_str_hash(Blockchain& core_storage, get_tx_pub_key_from_str_hash(Blockchain& core_storage,
const string& hash_str, const string& hash_str,
transaction& tx); transaction& tx);
bool bool
parse_str_address(const string& address_str, parse_str_address(const string& address_str,
account_public_address& address, address_parse_info& address_info,
bool testnet = false); cryptonote::network_type nettype = cryptonote::network_type::MAINNET);
inline bool inline bool
is_separator(char c); is_separator(char c);
string string
print_address(const account_public_address& address, print_address(const address_parse_info& address,
bool testnet = false); cryptonote::network_type nettype = cryptonote::network_type::MAINNET);
string string
print_sig (const signature& sig); print_sig (const signature& sig);
string string
remove_trailing_path_separator(const string& in_path); remove_trailing_path_separator(const string& in_path);
bf::path bf::path
remove_trailing_path_separator(const bf::path& in_path); remove_trailing_path_separator(const bf::path& in_path);
string string
timestamp_to_str_gm(time_t timestamp, const char* format = "%F %T"); timestamp_to_str_gm(time_t timestamp, const char* format = "%F %T");
ostream& ostream&
operator<< (ostream& os, const account_public_address& addr); operator<< (ostream& os, const address_parse_info& addr_info);
string string
get_default_lmdb_folder(bool testnet = false); get_default_lmdb_folder(cryptonote::network_type nettype = cryptonote::network_type::MAINNET);
bool bool
generate_key_image(const crypto::key_derivation& derivation, generate_key_image(const crypto::key_derivation& derivation,
const std::size_t output_index, const std::size_t output_index,
const crypto::secret_key& sec_key, const crypto::secret_key& sec_key,
const crypto::public_key& pub_key, const crypto::public_key& pub_key,
crypto::key_image& key_img); crypto::key_image& key_img);
bool bool
get_blockchain_path(const boost::optional<string>& bc_path, get_blockchain_path(const boost::optional<string>& bc_path,
bf::path& blockchain_path, bf::path& blockchain_path,
bool testnet = false); cryptonote::network_type nettype = cryptonote::network_type::MAINNET);
uint64_t uint64_t
sum_money_in_outputs(const transaction& tx); sum_money_in_outputs(const transaction& tx);
pair<uint64_t, uint64_t> pair<uint64_t, uint64_t>
sum_money_in_outputs(const string& json_str); sum_money_in_outputs(const string& json_str);
pair<uint64_t, uint64_t> pair<uint64_t, uint64_t>
sum_money_in_outputs(const json& _json); sum_money_in_outputs(const json& _json);
array<uint64_t, 4> array<uint64_t, 4>
summary_of_in_out_rct( summary_of_in_out_rct(
const transaction& tx, const transaction& tx,
vector<pair<txout_to_key, uint64_t>>& output_pub_keys, vector<pair<txout_to_key, uint64_t>>& output_pub_keys,
vector<txin_to_key>& input_key_imgs); vector<txin_to_key>& input_key_imgs);
// this version for mempool txs from json // this version for mempool txs from json
array<uint64_t, 6> array<uint64_t, 6>
summary_of_in_out_rct(const json& _json); summary_of_in_out_rct(const json& _json);
uint64_t uint64_t
sum_money_in_inputs(const transaction& tx); sum_money_in_inputs(const transaction& tx);
pair<uint64_t, uint64_t> pair<uint64_t, uint64_t>
sum_money_in_inputs(const string& json_str); sum_money_in_inputs(const string& json_str);
pair<uint64_t, uint64_t> pair<uint64_t, uint64_t>
sum_money_in_inputs(const json& _json); sum_money_in_inputs(const json& _json);
uint64_t uint64_t
count_nonrct_inputs(const transaction& tx); count_nonrct_inputs(const transaction& tx);
uint64_t uint64_t
count_nonrct_inputs(const string& json_str); count_nonrct_inputs(const string& json_str);
uint64_t uint64_t
count_nonrct_inputs(const json& _json); count_nonrct_inputs(const json& _json);
array<uint64_t, 2> array<uint64_t, 2>
sum_money_in_tx(const transaction& tx); sum_money_in_tx(const transaction& tx);
array<uint64_t, 2> array<uint64_t, 2>
sum_money_in_txs(const vector<transaction>& txs); sum_money_in_txs(const vector<transaction>& txs);
uint64_t uint64_t
sum_fees_in_txs(const vector<transaction>& txs); sum_fees_in_txs(const vector<transaction>& txs);
uint64_t uint64_t
get_mixin_no(const transaction& tx); get_mixin_no(const transaction& tx);
vector<uint64_t> vector<uint64_t>
get_mixin_no(const string& json_str); get_mixin_no(const string& json_str);
vector<uint64_t> vector<uint64_t>
get_mixin_no(const json& _json); get_mixin_no(const json& _json);
vector<uint64_t> vector<uint64_t>
get_mixin_no_in_txs(const vector<transaction>& txs); get_mixin_no_in_txs(const vector<transaction>& txs);
vector<pair<txout_to_key, uint64_t>> vector<pair<txout_to_key, uint64_t>>
get_ouputs(const transaction& tx); get_ouputs(const transaction& tx);
vector<tuple<txout_to_key, uint64_t, uint64_t>> vector<tuple<txout_to_key, uint64_t, uint64_t>>
get_ouputs_tuple(const transaction& tx); get_ouputs_tuple(const transaction& tx);
vector<txin_to_key> vector<txin_to_key>
get_key_images(const transaction& tx); get_key_images(const transaction& tx);
bool bool
get_payment_id(const vector<uint8_t>& extra, get_payment_id(const vector<uint8_t>& extra,
crypto::hash& payment_id, crypto::hash& payment_id,
crypto::hash8& payment_id8); crypto::hash8& payment_id8);
bool bool
get_payment_id(const transaction& tx, get_payment_id(const transaction& tx,
crypto::hash& payment_id, crypto::hash& payment_id,
crypto::hash8& payment_id8); crypto::hash8& payment_id8);
inline double inline double
get_xmr(uint64_t core_amount) get_xmr(uint64_t core_amount)
{ {
return static_cast<double>(core_amount) / 1e12; return static_cast<double>(core_amount) / 1e12;
} }
array<size_t, 5> array<size_t, 5>
timestamp_difference(uint64_t t1, uint64_t t2); timestamp_difference(uint64_t t1, uint64_t t2);
string string
read(string filename); read(string filename);
pair<string, double> pair<string, double>
timestamps_time_scale(const vector<uint64_t>& timestamps, timestamps_time_scale(const vector<uint64_t>& timestamps,
uint64_t timeN, uint64_t resolution = 80, uint64_t timeN, uint64_t resolution = 80,
uint64_t time0 = 1397818193 /* timestamp of the second block */); uint64_t time0 = 1397818193 /* timestamp of the second block */);
bool bool
decode_ringct(const rct::rctSig & rv, decode_ringct(const rct::rctSig & rv,
const crypto::public_key pub, const crypto::public_key pub,
const crypto::secret_key &sec, const crypto::secret_key &sec,
unsigned int i, unsigned int i,
rct::key & mask, rct::key & mask,
uint64_t & amount); uint64_t & amount);
bool bool
url_decode(const std::string& in, std::string& out); decode_ringct(const rct::rctSig & rv,
const crypto::key_derivation &derivation,
unsigned int i,
rct::key & mask,
uint64_t & amount);
bool
url_decode(const std::string& in, std::string& out);
map<std::string, std::string> map<std::string, std::string>
parse_crow_post_data(const string& req_body); parse_crow_post_data(const string& req_body);
// from wallet2::decrypt // from wallet2::decrypt
string string
decrypt(const std::string &ciphertext, decrypt(const std::string &ciphertext,
const crypto::secret_key &skey, const crypto::secret_key &skey,
bool authenticated = true); bool authenticated = true);
// based on // based on
// crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const // crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const
public_key public_key
get_tx_pub_key_from_received_outs(const transaction &tx); get_tx_pub_key_from_received_outs(const transaction &tx);
static static
string string
xmr_amount_to_str(const uint64_t& xmr_amount, xmr_amount_to_str(const uint64_t& xmr_amount,
string _format="{:0.12f}", string _format="{:0.12f}",
bool zero_to_question_mark=true) bool zero_to_question_mark=true)
{ {
string amount_str = "?"; string amount_str = "?";
if (!zero_to_question_mark) if (!zero_to_question_mark)
@ -265,17 +292,17 @@ namespace xmreg
} }
return amount_str; return amount_str;
} }
bool bool
is_output_ours(const size_t& output_index, is_output_ours(const size_t& output_index,
const transaction& tx, const transaction& tx,
const public_key& pub_tx_key, const public_key& pub_tx_key,
const secret_key& private_view_key, const secret_key& private_view_key,
const public_key& public_spend_key); const public_key& public_spend_key);
bool bool
get_real_output_for_key_image(const key_image& ki, get_real_output_for_key_image(const key_image& ki,
const transaction& tx, const transaction& tx,
const secret_key& private_view_key, const secret_key& private_view_key,
const public_key& public_spend_key, const public_key& public_spend_key,
@ -283,12 +310,12 @@ namespace xmreg
public_key output_pub_key); public_key output_pub_key);
// based on http://stackoverflow.com/a/9943098/248823 // based on http://stackoverflow.com/a/9943098/248823
template<typename Iterator, typename Func> template<typename Iterator, typename Func>
void chunks(Iterator begin, void chunks(Iterator begin,
Iterator end, Iterator end,
iterator_traits<string::iterator>::difference_type k, iterator_traits<string::iterator>::difference_type k,
Func f) Func f)
{ {
Iterator chunk_begin; Iterator chunk_begin;
Iterator chunk_end; Iterator chunk_end;
chunk_end = chunk_begin = begin; chunk_end = chunk_begin = begin;
@ -303,33 +330,45 @@ namespace xmreg
chunk_begin = chunk_end; chunk_begin = chunk_end;
} }
while(std::distance(chunk_begin,end) > 0); while(std::distance(chunk_begin,end) > 0);
} }
bool /*
make_tx_from_json(const string& json_str, transaction& tx); * Remove all characters in in_str that match the given
* regular expression
*/
template <typename T>
inline string
remove_bad_chars(T&& in_str, std::regex const& rgx = std::regex ("[^a-zA-Z0-9]"))
{
return std::regex_replace(std::forward<T>(in_str), rgx, "");
}
bool
make_tx_from_json(const string& json_str, transaction& tx);
string string
make_printable(const string& in_s); make_printable(const string& in_s);
string string
get_human_readable_timestamp(uint64_t ts); get_human_readable_timestamp(uint64_t ts);
// Get the median of an unordered set of numbers of arbitrary // Get the median of an unordered set of numbers of arbitrary
// type without modifying the underlying dataset. // type without modifying the underlying dataset.
// taken from http://stackoverflow.com/a/19695285 // taken from http://stackoverflow.com/a/19695285
template <typename It> template <typename It>
typename std::iterator_traits<It>::value_type typename std::iterator_traits<It>::value_type
calc_median(It it_begin, It it_end) calc_median(It it_begin, It it_end)
{ {
using T = typename std::iterator_traits<It>::value_type; using T = typename std::iterator_traits<It>::value_type;
std::vector<T> data(it_begin, it_end); std::vector<T> data(it_begin, it_end);
std::nth_element(data.begin(), data.begin() + data.size() / 2, data.end()); std::nth_element(data.begin(), data.begin() + data.size() / 2, data.end());
return data[data.size() / 2]; return data[data.size() / 2];
} }
void void
pause_execution(uint64_t no_seconds, const string& text = "now"); pause_execution(uint64_t no_seconds, const string& text = "now");
} }

Loading…
Cancel
Save