Merge pull request #86 from moneroexamples/devel

Add support for Monero 0.12.0, sub-addresses, JS decoding/proving txs and stagenet
This commit is contained in:
moneroexamples 2018-04-05 07:48:55 +08:00 committed by GitHub
commit 53e50edc61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 14052 additions and 7376 deletions

1
.gitignore vendored
View File

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

View File

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

View File

@ -8,7 +8,7 @@ special importance to privacy-oriented users:
- track users activates through google analytics,
- are closed sourced,
- are not available as hidden services,
- do not support Monero testnet,
- do not support Monero testnet nor stagenet networks,
- have limited JSON API.
@ -29,7 +29,7 @@ Tor users:
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://MoneroExplorer.com/](https://moneroexplorer.com/) - 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://explorer.monero-otc.com/](https://explorer.monero-otc.com/) - https enabled.
- [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.
- [https://anunknownamount.com](https://anunknownamount.com/) - looks nice, https enabled.
- [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 (down currently).
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://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):
@ -62,7 +67,8 @@ Alternative block explorers:
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,
- made fully in C++,
- showing encrypted payments ID,
@ -71,14 +77,14 @@ The key features of the Onion Monero Blockchain Explorer are:
- showing public components of Monero addresses,
- decoding which outputs and mixins belong to the given Monero address and viewkey,
- can prove that you send Monero to someone,
- detailed information about mixins, such as, mixins'
age, timescale, mixin of mixins,
- detailed information about mixins, such as, mixins' age, timescale, mixin of mixins,
- showing number of amount output indices,
- support Monero testnet network,
- tx checker and pusher for online pushing of transactions,
- estimate possible spendings based on address and viewkey,
- can provide total amount of all miner fees.
- decoding encrypted payment id.
- decoding outputs and proving txs sent to sub-address.
## Development branch
@ -91,7 +97,7 @@ Current development branch, which includes support for sub-addresses is:
## 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:
@ -99,17 +105,17 @@ Download and compile recent Monero into your home folder:
# first install monero dependecines
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
cd ~
git clone https://github.com/monero-project/monero
git clone --recursive https://github.com/monero-project/monero
cd monero/
# checkout last monero version
git checkout -b last_release v0.11.1.0
#git checkout -b last_release v0.12.0.0
make
```
@ -137,6 +143,9 @@ cmake ..
# altearnatively can use: cmake -DMONERO_DIR=/path/to/monero_folder ..
# if monero is not in ~/monero
#
# also can build with ASAN (sanitizers), for example
# cmake -DSANITIZE_ADDRESS=On ..
# compile
make
@ -170,6 +179,7 @@ Go to your browser: http://127.0.0.1:8081
xmrblocks, Onion Monero Blockchain Explorer:
-h [ --help ] [=arg(=1)] (=0) produce help message
-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-mixin-details [=arg(=1)] (=0)
enable mixin details for key images,
@ -179,11 +189,13 @@ xmrblocks, Onion Monero Blockchain Explorer:
enable key images file checker
--enable-output-key-checker [=arg(=1)] (=0)
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
--show-cache-times [=arg(=1)] (=0) show times of getting data from cache
vs no cache
--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 users to have the index page on
autorefresh
@ -192,11 +204,14 @@ xmrblocks, Onion Monero Blockchain Explorer:
thread
-p [ --port ] arg (=8081) default explorer port
--testnet-url arg you can specify testnet url, if you run
it on mainnet. link will show on front
page to testnet explorer
it on mainnet or stagenet. link will
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
it on testnet. link will show on front
page to mainnet explorer
it on testnet or stagenet. link will
show on front page to mainnet explorer
--no-blocks-on-index arg (=10) number of last blocks to be shown on
index page
--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.
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,
in `~/.bitmonero/lmdb/emission_amount.txt`. For testnet network,
it is `~/.bitmonero/testnet/lmdb/emission_amount.txt`. This file is used so that we don't
in `~/.bitmonero/lmdb/emission_amount.txt`. For testnet or stagenet networks,
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
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
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
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
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
@ -263,6 +278,15 @@ The values given, can be checked using Monero daemon's `print_coinbase_tx_sum`
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.
## 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)
@ -290,6 +314,11 @@ for any practical use need to have properly issued ssl certificates.
## JSON API
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>

59
cmake/FindASan.cmake Normal file
View File

@ -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 ()

57
cmake/FindMSan.cmake Normal file
View File

@ -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 ()

View File

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

View File

@ -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)

65
cmake/FindTSan.cmake Normal file
View File

@ -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 ()

46
cmake/FindUBSan.cmake Normal file
View File

@ -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 ()

55
cmake/asan-wrapper Executable file
View File

@ -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 $@

View File

@ -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 ()

250
ext/crow/crow.h Executable file → Normal file
View File

@ -1,229 +1,23 @@
#pragma once
#include <string>
#include <functional>
#include <memory>
#include <future>
#include <cstdint>
#include <type_traits>
#include <thread>
#include "settings.h"
#include "logging.h"
#include "utility.h"
#include "routing.h"
#include "middleware_context.h"
#include "http_request.h"
#include "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_->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<>;
}
#include "crow/query_string.h"
#include "crow/http_parser_merged.h"
#include "crow/ci_map.h"
#include "crow/TinySHA1.hpp"
#include "crow/settings.h"
#include "crow/socket_adaptors.h"
#include "crow/json.h"
#include "crow/mustache.h"
#include "crow/logging.h"
#include "crow/dumb_timer_queue.h"
#include "crow/utility.h"
#include "crow/common.h"
#include "crow/http_request.h"
#include "crow/websocket.h"
#include "crow/parser.h"
#include "crow/http_response.h"
#include "crow/middleware.h"
#include "crow/routing.h"
#include "crow/middleware_context.h"
#include "crow/http_connection.h"
#include "crow/http_server.h"
#include "crow/app.h"

0
ext/crow/TinySHA1.hpp → ext/crow/crow/TinySHA1.hpp Executable file → Normal file
View File

248
ext/crow/crow/app.h Normal file
View File

@ -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<>;
}

7
ext/crow/common.h → ext/crow/crow/common.h Executable file → Normal file
View File

@ -4,7 +4,7 @@
#include <string>
#include <stdexcept>
#include <iostream>
#include "utility.h"
#include "crow/utility.h"
namespace crow
{
@ -19,6 +19,7 @@ namespace crow
CONNECT,
OPTIONS,
TRACE,
PATCH = 24,
#endif
Delete = 0,
@ -29,6 +30,7 @@ namespace crow
Connect,
Options,
Trace,
Patch = 24,
};
inline std::string method_name(HTTPMethod method)
@ -51,6 +53,8 @@ namespace crow
return "OPTIONS";
case HTTPMethod::Trace:
return "TRACE";
case HTTPMethod::Patch:
return "PATCH";
}
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, "CONNECT", 7) ? crow::HTTPMethod::Connect :
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");
}
#endif

View File

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

View File

@ -7,15 +7,15 @@
#include <chrono>
#include <vector>
#include "http_parser_merged.h"
#include "crow/http_parser_merged.h"
#include "parser.h"
#include "http_response.h"
#include "logging.h"
#include "settings.h"
#include "dumb_timer_queue.h"
#include "middleware_context.h"
#include "socket_adaptors.h"
#include "crow/parser.h"
#include "crow/http_response.h"
#include "crow/logging.h"
#include "crow/settings.h"
#include "crow/dumb_timer_queue.h"
#include "crow/middleware_context.h"
#include "crow/socket_adaptors.h"
namespace crow
{
@ -176,7 +176,7 @@ namespace crow
}
#ifdef CROW_ENABLE_DEBUG
static int connectionCount;
static std::atomic<int> connectionCount;
#endif
template <typename Adaptor, typename Handler, typename ... Middlewares>
class Connection
@ -374,6 +374,9 @@ namespace crow
{401, "HTTP/1.1 401 Unauthorized\r\n"},
{403, "HTTP/1.1 403 Forbidden\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"},
{501, "HTTP/1.1 501 Not Implemented\r\n"},
@ -467,7 +470,7 @@ namespace crow
if (!ec)
{
bool ret = parser_.feed(buffer_.data(), bytes_transferred);
if (ret && adaptor_.is_open() && !close_connection_)
if (ret && adaptor_.is_open())
{
error_while_reading = false;
}
@ -482,6 +485,14 @@ namespace crow
CROW_LOG_DEBUG << this << " from read(1)";
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_)
{
start_deadline();

View File

View File

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

View File

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

67
ext/crow/http_server.h → ext/crow/crow/http_server.h Executable file → Normal file
View File

@ -1,5 +1,6 @@
#pragma once
#include <chrono>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/asio.hpp>
#ifdef CROW_ENABLE_SSL
@ -12,9 +13,9 @@
#include <memory>
#include "http_connection.h"
#include "logging.h"
#include "dumb_timer_queue.h"
#include "crow/http_connection.h"
#include "crow/logging.h"
#include "crow/dumb_timer_queue.h"
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)
: acceptor_(io_service_, tcp::endpoint(boost::asio::ip::address::from_string(bindaddr), port)),
signals_(io_service_, SIGINT, SIGTERM),
tick_timer_(io_service_),
handler_(handler),
concurrency_(concurrency),
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()
{
if (concurrency_ < 0)
@ -101,15 +121,36 @@ namespace crow
timer.async_wait(handler);
init_count ++;
try
while(1)
{
io_service_pool_[i]->run();
} catch(std::exception& e)
{
CROW_LOG_ERROR << "Worker Crash: An uncaught exception occurred: " << e.what();
try
{
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)
{
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(
[&](const boost::system::error_code& /*error*/, int /*signal_number*/){
@ -161,6 +202,10 @@ namespace crow
p->start();
});
}
else
{
delete p;
}
do_accept();
});
}
@ -172,6 +217,7 @@ namespace crow
std::vector<std::function<std::string()>> get_cached_date_str_pool_;
tcp::acceptor acceptor_;
boost::asio::signal_set signals_;
boost::asio::deadline_timer tick_timer_;
Handler* handler_;
uint16_t concurrency_{1};
@ -180,6 +226,9 @@ namespace crow
std::string bindaddr_;
unsigned int roundrobin_index_{};
std::chrono::milliseconds tick_interval_;
std::function<void()> tick_function_;
std::tuple<Middlewares...>* middlewares_;
#ifdef CROW_ENABLE_SSL

37
ext/crow/json.h → ext/crow/crow/json.h Executable file → Normal file
View File

@ -12,6 +12,8 @@
#include <boost/operators.hpp>
#include <vector>
#include "crow/settings.h"
#if defined(__GNUG__) || defined(__clang__)
#define crow_json_likely(x) __builtin_expect(x, 1)
#define crow_json_unlikely(x) __builtin_expect(x, 0)
@ -1262,6 +1264,23 @@ namespace crow
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>
wvalue& operator=(const std::vector<T>& v)
{
@ -1314,6 +1333,18 @@ namespace crow
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
{
switch(t_)
@ -1354,7 +1385,6 @@ namespace crow
return 1;
}
friend void dump_internal(const wvalue& v, std::string& out);
friend std::string dump(const wvalue& v);
};
@ -1375,7 +1405,12 @@ namespace crow
case type::Number:
{
char outbuf[128];
#ifdef _MSC_VER
sprintf_s(outbuf, 128, "%g", v.d);
#else
sprintf(outbuf, "%g", v.d);
#endif
out += outbuf;
}
break;

2
ext/crow/logging.h → ext/crow/crow/logging.h Executable file → Normal file
View File

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

89
ext/crow/middleware.h → ext/crow/crow/middleware.h Executable file → Normal file
View File

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

View File

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

7
ext/crow/mustache.h → ext/crow/crow/mustache.h Executable file → Normal file
View File

@ -4,7 +4,7 @@
#include <fstream>
#include <iterator>
#include <functional>
#include "json.h"
#include "crow/json.h"
namespace crow
{
namespace mustache
@ -555,6 +555,11 @@ namespace crow
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)
{
return compile(detail::get_loader_ref()(filename));

5
ext/crow/parser.h → ext/crow/crow/parser.h Executable file → Normal file
View File

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

View File

@ -4,8 +4,12 @@
#include <string.h>
#include <string>
#include <vector>
#include <unordered_map>
#include <iostream>
#include <boost/optional.hpp>
namespace crow
{
// ----------------------------------------------------------------------------
// qs_parse (modified)
// 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;
}
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)
{
@ -220,8 +266,12 @@ inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t
{
qs++;
i = strcspn(qs, "&=#");
strncpy(val, qs, (val_len-1)<(i+1) ? (val_len-1) : (i+1));
qs_decode(val);
#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);
}
else
{
@ -231,6 +281,7 @@ inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t
return val;
}
}
// ----------------------------------------------------------------------------
@ -333,6 +384,20 @@ namespace crow
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:
std::string url_;

59
ext/crow/routing.h → ext/crow/crow/routing.h Executable file → Normal file
View File

@ -8,12 +8,12 @@
#include <boost/lexical_cast.hpp>
#include <vector>
#include "common.h"
#include "http_response.h"
#include "http_request.h"
#include "utility.h"
#include "logging.h"
#include "websocket.h"
#include "crow/common.h"
#include "crow/http_response.h"
#include "crow/http_request.h"
#include "crow/utility.h"
#include "crow/logging.h"
#include "crow/websocket.h"
namespace crow
{
@ -156,7 +156,7 @@ namespace crow
struct Wrapped
{
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
, int>::type = 0)
{
@ -190,7 +190,7 @@ namespace crow
};
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<1, std::tuple<Args..., void, void>>::type, response&>::value
, int>::type = 0)
@ -205,7 +205,7 @@ namespace crow
}
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<1, std::tuple<Args..., void, void>>::type, response&>::value
, int>::type = 0)
@ -276,12 +276,12 @@ namespace crow
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
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
@ -313,11 +313,19 @@ namespace crow
return *this;
}
template <typename Func>
self_t& onaccept(Func f)
{
accept_handler_ = f;
return *this;
}
protected:
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&)> close_handler_;
std::function<void(crow::websocket::connection&)> error_handler_;
std::function<bool(const crow::request&)> accept_handler_;
};
template <typename T>
@ -394,7 +402,7 @@ namespace crow
#else
template <typename Func, unsigned ... Indices>
#endif
std::function<void(const request&, response&, const routing_params&)>
std::function<void(const request&, response&, const routing_params&)>
wrap(Func f, black_magic::seq<Indices...>)
{
#ifdef CROW_MSVC_WORKAROUND
@ -403,14 +411,16 @@ namespace crow
using function_t = utility::function_traits<Func>;
#endif
if (!black_magic::is_parameter_tag_compatible(
black_magic::get_parameter_tag_runtime(rule_.c_str()),
black_magic::get_parameter_tag_runtime(rule_.c_str()),
black_magic::compute_parameter_tag_from_args_list<
typename function_t::template arg<Indices>...>::value))
{
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>...>();
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;
}
@ -454,10 +464,16 @@ namespace crow
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_ = [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.end();
};
});
}
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,
"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.end();
};
});
}
template <typename Func>
@ -981,7 +1003,6 @@ public:
auto found = trie_.find(req.url);
unsigned rule_index = found.first;
CROW_LOG_DEBUG << "???" << rule_index;
if (!rule_index)
{

View File

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

View File

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

6
ext/crow/utility.h → ext/crow/crow/utility.h Executable file → Normal file
View File

@ -8,6 +8,8 @@
#include <functional>
#include <string>
#include "crow/settings.h"
namespace crow
{
namespace black_magic
@ -500,7 +502,7 @@ template <typename F, typename Set>
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;
ret.resize((size+2) / 3 * 4);
@ -536,7 +538,7 @@ template <typename F, typename Set>
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-_");
}

67
ext/crow/websocket.h → ext/crow/crow/websocket.h Executable file → Normal file
View File

@ -1,7 +1,9 @@
#pragma once
#include "socket_adaptors.h"
#include "http_request.h"
#include "TinySHA1.hpp"
#include <boost/algorithm/string/predicate.hpp>
#include <boost/array.hpp>
#include "crow/socket_adaptors.h"
#include "crow/http_request.h"
#include "crow/TinySHA1.hpp"
namespace crow
{
@ -22,6 +24,12 @@ namespace crow
virtual void send_text(const std::string& msg) = 0;
virtual void close(const std::string& msg = "quit") = 0;
virtual ~connection(){}
void userdata(void* u) { userdata_ = u; }
void* userdata() { return userdata_; }
private:
void* userdata_;
};
template <typename Adaptor>
@ -32,15 +40,28 @@ namespace crow
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&)> 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))
, accept_handler_(std::move(accept_handler))
{
if (req.get_header_value("upgrade") != "websocket")
if (!boost::iequals(req.get_header_value("upgrade"), "websocket"))
{
adaptor.close();
delete this;
return;
}
if (accept_handler_)
{
if (!accept_handler_(req))
{
adaptor.close();
delete this;
return;
}
}
// Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
// Sec-WebSocket-Version: 13
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)
{
buf[1] += 126;
*(uint16_t*)(buf+2) = (uint16_t)size;
*(uint16_t*)(buf+2) = htons((uint16_t)size);
return {buf, buf+4};
}
else
{
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};
}
}
@ -162,7 +183,12 @@ namespace crow
{
//boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&mini_header_, 1),
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;
mini_header_ = htons(mini_header_);
@ -205,11 +231,17 @@ namespace crow
case WebSocketReadState::Len16:
{
remaining_length_ = 0;
boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&remaining_length_, 2),
[this](const boost::system::error_code& ec, std::size_t bytes_transferred)
uint16_t remaining_length16_ = 0;
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;
remaining_length_ = ntohs(*(uint16_t*)&remaining_length_);
remaining_length16_ = ntohs(remaining_length16_);
remaining_length_ = remaining_length16_;
#ifdef CROW_ENABLE_DEBUG
if (!ec && bytes_transferred != 2)
{
@ -236,7 +268,11 @@ namespace crow
case WebSocketReadState::Len64:
{
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;
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;
case WebSocketReadState::Mask:
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;
#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&)> close_handler_;
std::function<void(crow::websocket::connection&)> error_handler_;
std::function<bool(const crow::request&)> accept_handler_;
};
}
}

195
main.cpp
View File

@ -11,6 +11,7 @@
#include <regex>
using boost::filesystem::path;
using xmreg::remove_bad_chars;
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 no_blocks_on_index_opt = opts.get_option<string>("no-blocks-on-index");
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 mempool_info_timeout_opt = opts.get_option<string>("mempool-info-timeout");
auto mempool_refresh_time_opt = opts.get_option<string>("mempool-refresh-time");
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_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_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_json_api_opt = opts.get_option<bool>("enable-json-api");
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 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_js {*enable_js_opt};
bool enable_key_image_checker {*enable_key_image_checker_opt};
bool enable_autorefresh_option {*enable_autorefresh_option_opt};
bool enable_output_key_checker {*enable_output_key_checker_opt};
@ -125,7 +142,7 @@ main(int ac, const char* av[])
// get 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;
return EXIT_FAILURE;
@ -150,16 +167,15 @@ main(int ac, const char* av[])
string deamon_url {*deamon_url_opt};
if (testnet && deamon_url == "http:://127.0.0.1:18081")
{
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};
try
{
mempool_info_timeout = boost::lexical_cast<uint64_t>(*mempool_info_timeout_opt);
}
catch (boost::bad_lexical_cast &e)
{
@ -187,8 +203,8 @@ main(int ac, const char* av[])
xmreg::CurrentBlockchainStatus::blockchain_path
= blockchain_path;
xmreg::CurrentBlockchainStatus::testnet
= testnet;
xmreg::CurrentBlockchainStatus::nettype
= nettype;
xmreg::CurrentBlockchainStatus::deamon_url
= deamon_url;
xmreg::CurrentBlockchainStatus::set_blockchain_variables(
@ -204,8 +220,8 @@ main(int ac, const char* av[])
xmreg::MempoolStatus::blockchain_path
= blockchain_path;
xmreg::MempoolStatus::testnet
= testnet;
xmreg::MempoolStatus::nettype
= nettype;
xmreg::MempoolStatus::deamon_url
= deamon_url;
xmreg::MempoolStatus::set_blockchain_variables(
@ -236,8 +252,9 @@ main(int ac, const char* av[])
xmreg::page xmrblocks(&mcore,
core_storage,
deamon_url,
testnet,
nettype,
enable_pusher,
enable_js,
enable_key_image_checker,
enable_output_key_checker,
enable_autorefresh_option,
@ -248,6 +265,7 @@ main(int ac, const char* av[])
no_blocks_on_index,
mempool_info_timeout,
*testnet_url,
*stagenet_url,
*mainnet_url);
// crow instance
@ -276,21 +294,23 @@ main(int ac, const char* av[])
CROW_ROUTE(app, "/block/<string>")
([&](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>")
([&](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>")
([&](string tx_hash, uint16_t with_ring_signatures) {
return xmrblocks.show_tx(tx_hash, with_ring_signatures);
([&](string tx_hash, uint16_t with_ring_signatures)
{
return xmrblocks.show_tx(remove_bad_chars(tx_hash), with_ring_signatures);
});
CROW_ROUTE(app, "/myoutputs").methods("POST"_method)
([&](const crow::request& req) {
([&](const crow::request& req)
{
map<std::string, std::string> post_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");
}
string tx_hash = post_body["tx_hash"];
string xmr_address = post_body["xmr_address"];
string viewkey = post_body["viewkey"];
string tx_hash = remove_bad_chars(post_body["tx_hash"]);
string xmr_address = remove_bad_chars(post_body["xmr_address"]);
string viewkey = remove_bad_chars(post_body["viewkey"]);
// this will be only not empty when checking raw tx data
// 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);
@ -319,12 +339,15 @@ main(int ac, const char* av[])
CROW_ROUTE(app, "/myoutputs/<string>/<string>/<string>")
([&](const crow::request& req, string tx_hash,
string xmr_address, string viewkey) {
string xmr_address, string viewkey)
{
string domain = get_domain(req);
return xmrblocks.show_my_outputs(tx_hash, xmr_address,
viewkey, string {},
return xmrblocks.show_my_outputs(remove_bad_chars(tx_hash),
remove_bad_chars(xmr_address),
remove_bad_chars(viewkey),
string {},
domain);
});
@ -342,14 +365,21 @@ main(int ac, const char* av[])
"tx hash not provided");
}
string tx_hash = post_body["txhash"];;
string tx_prv_key = post_body["txprvkey"];;
string xmr_address = post_body["xmraddress"];;
string tx_hash = remove_bad_chars(post_body["txhash"]);
string tx_prv_key = remove_bad_chars(post_body["txprvkey"]);
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);
return xmrblocks.show_prove(tx_hash, xmr_address,
tx_prv_key, domain);
return xmrblocks.show_prove(tx_hash,
xmr_address,
tx_prv_key,
raw_tx_data,
domain);
});
@ -359,8 +389,11 @@ main(int ac, const char* av[])
string domain = get_domain(req);
return xmrblocks.show_prove(tx_hash, xmr_address,
tx_prv_key, domain);
return xmrblocks.show_prove(remove_bad_chars(tx_hash),
remove_bad_chars(xmr_address),
remove_bad_chars(tx_prv_key),
string {},
domain);
});
if (enable_pusher)
@ -381,13 +414,14 @@ main(int ac, const char* av[])
return string("Raw tx data or action not provided");
}
string raw_tx_data = post_body["rawtxdata"];
string action = post_body["action"];
string raw_tx_data = remove_bad_chars(post_body["rawtxdata"]);
string action = remove_bad_chars(post_body["action"]);
if (action == "check")
return xmrblocks.show_checkrawtx(raw_tx_data, action);
else if (action == "push")
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");
}
string raw_data = post_body["rawkeyimgsdata"];
string viewkey = post_body["viewkey"];
string raw_data = remove_bad_chars(post_body["rawkeyimgsdata"]);
string viewkey = remove_bad_chars(post_body["viewkey"]);
return xmrblocks.show_checkrawkeyimgs(raw_data, viewkey);
});
@ -447,8 +481,8 @@ main(int ac, const char* av[])
"key image file without it");
}
string raw_data = post_body["rawoutputkeysdata"];
string viewkey = post_body["viewkey"];
string raw_data = remove_bad_chars(post_body["rawoutputkeysdata"]);
string viewkey = remove_bad_chars(post_body["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)
([&](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")
@ -483,12 +517,73 @@ main(int ac, const char* av[])
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)
{
cout << "Enable JSON API\n";
CROW_ROUTE(app, "/api/transaction/<string>")
([&](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;
});
@ -496,7 +591,7 @@ main(int ac, const char* av[])
CROW_ROUTE(app, "/api/rawtransaction/<string>")
([&](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;
});
@ -504,7 +599,7 @@ main(int ac, const char* av[])
CROW_ROUTE(app, "/api/block/<string>")
([&](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;
});
@ -512,7 +607,7 @@ main(int ac, const char* av[])
CROW_ROUTE(app, "/api/rawblock/<string>")
([&](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;
});
@ -526,7 +621,8 @@ main(int ac, const char* av[])
string limit = regex_search(req.raw_url, regex {"limit=\\d+"}) ?
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;
});
@ -543,7 +639,8 @@ main(int ac, const char* av[])
string limit = regex_search(req.raw_url, regex {"limit=\\d+"}) ?
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;
});
@ -551,7 +648,7 @@ main(int ac, const char* av[])
CROW_ROUTE(app, "/api/search/<string>")
([&](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;
});
@ -597,7 +694,11 @@ main(int ac, const char* av[])
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;
});
@ -627,7 +728,11 @@ main(int ac, const char* av[])
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;
});
@ -640,7 +745,7 @@ main(int ac, const char* av[])
return r;
});
}
} // if (enable_json_api)
if (enable_autorefresh_option)
{

View File

@ -25,6 +25,8 @@ namespace xmreg
"produce help message")
("testnet,t", value<bool>()->default_value(false)->implicit_value(true),
"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 signed transaction pusher")
("enable-mixin-details", value<bool>()->default_value(false)->implicit_value(true),
@ -33,7 +35,7 @@ namespace xmreg
"enable key images file checker")
("enable-output-key-checker", value<bool>()->default_value(false)->implicit_value(true),
"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-tx-cache", value<bool>()->default_value(false)->implicit_value(true),
"enable caching of transaction details")
@ -41,6 +43,8 @@ namespace xmreg
"show times of getting data from cache vs no cache")
("enable-block-cache", value<bool>()->default_value(false)->implicit_value(true),
"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 users to have the index page on autorefresh")
("enable-emission-monitor", value<bool>()->default_value(false)->implicit_value(true),
@ -48,9 +52,11 @@ namespace xmreg
("port,p", value<string>()->default_value("8081"),
"default explorer port")
("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(""),
"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"),
"number of last blocks to be shown on index page")
("mempool-info-timeout", value<string>()->default_value("5000"),

View File

@ -299,7 +299,7 @@ CurrentBlockchainStatus::is_thread_running()
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"};

View File

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

View File

@ -35,48 +35,48 @@ MempoolStatus::start_mempool_status_thread()
{
uint64_t loop_index {0};
// so that network status is checked every minute
uint64_t loop_index_divider = std::max<uint64_t>(1, 60 / mempool_refresh_time);
// so that network status is checked every minute
uint64_t loop_index_divider = std::max<uint64_t>(1, 60 / mempool_refresh_time);
while (true)
while (true)
{
// we just query network status every minute. No sense
// to do it as frequently as getting mempool data.
if (loop_index % loop_index_divider == 0)
{
// we just query network status every minute. No sense
// to do it as frequently as getting mempool data.
if (loop_index % loop_index_divider == 0)
if (!MempoolStatus::read_network_info())
{
if (!MempoolStatus::read_network_info())
{
network_info local_copy = current_network_info;
network_info local_copy = current_network_info;
cerr << " Cant read network info "<< endl;
cerr << " Cant read network info "<< endl;
local_copy.current = false;
local_copy.current = false;
current_network_info = local_copy;
}
else
{
cout << "Current network info read, ";
loop_index == 0;
}
current_network_info = local_copy;
}
if (MempoolStatus::read_mempool())
else
{
vector<mempool_tx> current_mempool_txs = get_mempool_txs();
cout << "mempool status txs: "
<< current_mempool_txs.size()
<< endl;
cout << "Current network info read, ";
loop_index = 0;
}
}
// when we reach top of the blockchain, update
// the emission amount every minute.
boost::this_thread::sleep_for(
boost::chrono::seconds(mempool_refresh_time));
if (MempoolStatus::read_mempool())
{
vector<mempool_tx> current_mempool_txs = get_mempool_txs();
++loop_index;
cout << "mempool status txs: "
<< current_mempool_txs.size()
<< endl;
}
// when we reach top of the blockchain, update
// the emission amount every minute.
boost::this_thread::sleep_for(
boost::chrono::seconds(mempool_refresh_time));
++loop_index;
} // while (true)
}
@ -84,7 +84,7 @@ MempoolStatus::start_mempool_status_thread()
{
cout << "Mempool status thread interrupted." << endl;
return;
}
}
}}; // m_thread = boost::thread{[]()
@ -126,63 +126,75 @@ MempoolStatus::read_mempool()
// get transaction info of the tx in the mempool
const tx_info& _tx_info = mempool_tx_info.at(i);
crypto::hash mem_tx_hash = null_hash;
transaction tx;
crypto::hash tx_hash;
crypto::hash tx_prefix_hash;
if (epee::string_tools::hex_to_pod(_tx_info.id_hash, mem_tx_hash))
if (!parse_and_validate_tx_from_blob(
_tx_info.tx_blob, tx, tx_hash, tx_prefix_hash))
{
transaction tx;
cerr << "Cant make tx from _tx_info.tx_blob" << endl;
return false;
}
if (!xmreg::make_tx_from_json(_tx_info.tx_json, tx))
{
cerr << "Cant make tx from _tx_info.tx_json" << endl;
return false;
}
mempool_size_kB += _tx_info.blob_size;
crypto::hash tx_hash_reconstructed = get_transaction_hash(tx);
local_copy_of_mempool_txs.push_back(mempool_tx {tx_hash, tx});
if (mem_tx_hash != tx_hash_reconstructed)
{
cerr << "Hash of reconstructed tx from json does not match "
"what we should get!"
<< endl;
mempool_tx& last_tx = local_copy_of_mempool_txs.back();
return false;
}
// key images of inputs
vector<txin_to_key> input_key_imgs;
mempool_size_kB += _tx_info.blob_size;
// public keys and xmr amount of outputs
vector<pair<txout_to_key, uint64_t>> output_pub_keys;
local_copy_of_mempool_txs.push_back(mempool_tx {tx_hash_reconstructed, tx});
// sum xmr in inputs and ouputs in the given tx
const array<uint64_t, 4>& sum_data = summary_of_in_out_rct(
tx, output_pub_keys, input_key_imgs);
mempool_tx& last_tx = local_copy_of_mempool_txs.back();
// key images of inputs
vector<txin_to_key> input_key_imgs;
// public keys and xmr amount of outputs
vector<pair<txout_to_key, uint64_t>> output_pub_keys;
double tx_size = static_cast<double>(_tx_info.blob_size)/1024.0;
// sum xmr in inputs and ouputs in the given tx
const array<uint64_t, 4>& sum_data = summary_of_in_out_rct(
tx, output_pub_keys, input_key_imgs);
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_inputs = sum_data[1];
last_tx.no_outputs = output_pub_keys.size();
last_tx.no_inputs = input_key_imgs.size();
last_tx.mixin_no = sum_data[2];
last_tx.num_nonrct_inputs = sum_data[3];
last_tx.sum_outputs = sum_data[0];
last_tx.sum_inputs = sum_data[1];
last_tx.no_outputs = output_pub_keys.size();
last_tx.no_inputs = input_key_imgs.size();
last_tx.mixin_no = sum_data[2];
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.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.timestamp_str = xmreg::timestamp_to_str_gm(_tx_info.receive_time);
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_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.txsize = fmt::format("{:0.2f}",
static_cast<double>(_tx_info.blob_size)/1024.0);
last_tx.txsize = fmt::format("{:0.2f}", tx_size);
} // 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)
@ -211,9 +223,7 @@ MempoolStatus::read_network_info()
COMMAND_RPC_GET_INFO::response rpc_network_info;
if (!rpc.get_network_info(rpc_network_info))
{
return false;
}
uint64_t fee_estimated;
@ -229,6 +239,11 @@ MempoolStatus::read_network_info()
(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;
@ -244,15 +259,19 @@ MempoolStatus::read_network_info()
local_copy.outgoing_connections_count = rpc_network_info.outgoing_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.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.block_size_limit = rpc_network_info.block_size_limit;
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);
local_copy.fee_per_kb = fee_estimated;
local_copy.info_timestamp = static_cast<uint64_t>(std::time(nullptr));
local_copy.current_hf_version = rpc_hardfork_info.version;
local_copy.current = true;
current_network_info = local_copy;
@ -285,7 +304,7 @@ MempoolStatus::is_thread_running()
bf::path MempoolStatus::blockchain_path {"/home/mwo/.bitmonero/lmdb"};
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};
boost::thread MempoolStatus::m_thread;
Blockchain* MempoolStatus::core_storage {nullptr};

View File

@ -38,10 +38,15 @@ struct MempoolStatus
uint64_t mixin_no {0};
string fee_str;
string payed_for_kB_str;
string xmr_inputs_str;
string xmr_outputs_str;
string timestamp_str;
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 white_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;
uint64_t cumulative_difficulty {0};
uint64_t block_size_limit {0};
uint64_t start_time {0};
uint64_t current_hf_version {0};
uint64_t hash_rate {0};
uint64_t fee_per_kb {0};
@ -114,7 +120,7 @@ struct MempoolStatus
static bf::path blockchain_path;
static string deamon_url;
static bool testnet;
static cryptonote::network_type nettype;
// make object for accessing the blockchain here
static MicroCore* mcore;

View File

@ -32,7 +32,9 @@ namespace xmreg
MicroCore::MicroCore():
m_mempool(m_blockchain_storage),
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
// 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;
}
hw::device* const
MicroCore::get_device() const
{
return m_device;
}
}

View File

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

View File

@ -16,15 +16,15 @@
#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 "storages/http_abstract_invoke.h"
//#include "cryptonote_core/cryptonote_basic.h"
#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/blockchain.h"
#include "blockchain_db/lmdb/db_lmdb.h"
#include "device/device_default.hpp"
#include "wallet/wallet2.h"

10732
src/page.h

File diff suppressed because it is too large Load Diff

View File

@ -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
rpccalls::get_dynamic_per_kb_fee_estimate(
uint64_t grace_blocks,

View File

@ -9,6 +9,7 @@
#include "monero_headers.h"
#include <mutex>
#include <utility>
@ -23,7 +24,7 @@ struct has_destructor
{
// has destructor
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();
}
@ -98,6 +99,9 @@ public:
bool
get_network_info(COMMAND_RPC_GET_INFO::response& info);
bool
get_hardfork_info( COMMAND_RPC_HARD_FORK_INFO::response& res);
bool
get_dynamic_per_kb_fee_estimate(
uint64_t grace_blocks,

View File

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

View File

@ -12,13 +12,15 @@
{{#css_styles}}{{/css_styles}}
</style>
{{#js_files}}{{/js_files}}
</head>
<body>
<div>
<div class="center">
<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>

View File

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

190
src/templates/js/base58.js Executable file
View File

@ -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

2219
src/templates/js/cn_util.js Executable file

File diff suppressed because it is too large Load Diff

30
src/templates/js/config.js Executable file
View File

@ -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
};

65
src/templates/js/crc32.js Executable file
View File

@ -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;
})();

22
src/templates/js/crypto.js Executable file

File diff suppressed because one or more lines are too long

5
src/templates/js/jquery.min.js vendored Executable file

File diff suppressed because one or more lines are too long

571
src/templates/js/nacl-fast-cn.js Executable file
View File

@ -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 || {}));

473
src/templates/js/sha3.js Executable file
View File

@ -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));

View File

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

View File

@ -14,7 +14,8 @@
<H5 style="margin:5px">Payment id (encrypted): {{payment_id8}}</H5>
{{/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}}
{{/has_payment_id8}}

View File

@ -5,15 +5,16 @@
{{#enable_mixins_details}}
<H5 style="margin:5px">Tx prefix hash: {{tx_prefix_hash}}</H5>
{{/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}}
<H5 style="margin:5px">Payment id: {{payment_id}}</H5>
<H5 style="margin:5px">Payment id as ascii ([a-zA-Z0-9 /!]): {{payment_id_as_ascii}}</H5>
<H5 style="margin:5px">Payment id: <span id="payment_id">{{payment_id}}</span></H5>
<H5 style="margin:5px">Payment id as ascii: {{payment_id_as_ascii}}</H5>
{{/has_payment_id}}
{{#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}}
@ -37,7 +38,7 @@
{{/have_raw_tx}}
<tr>
<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>
</tr>
<tr>
@ -48,7 +49,7 @@
<tr>
<td colspan="3">Extra: {{extra}}</td>
<td colspan="3" style="max-width:700px">Extra: {{extra}}</td>
</tr>
@ -81,19 +82,32 @@
<input type="radio" id="tab-1" name="tab-group-1" checked>
<label for="tab-1">Decode outputs</label>
<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">
For RingCT transactions, outputs' amounts are also decoded
<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>
<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="text" name="xmr_address" size="90" placeholder="Monero address"><br/>
<input type="text" name="viewkey" size="90" placeholder="Private viewkey" style="margin-top:5px"><br/>
<input type="text" name="xmr_address" size="110" placeholder="Monero address/subaddress"><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}}">
<!--above raw_tx_data field only used when checking raw tx data through tx pusher-->
<input type="submit" value="Decode outputs" style="margin-top:5px" >
{{#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" >
{{/enable_js}}
</form>
</div>
</div>
@ -108,13 +122,29 @@
Tx private key can be obtained using <i>get_tx_key</i>
command in <i>monero-wallet-cli</i> command line tool
<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>
<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="text" name="txprvkey" size="90" placeholder="Tx private key"><br/>
<input type="text" name="xmraddress" size="90" placeholder="Recipient's Monero Address" style="margin-top:5px"><br/>
<input type="submit" value="Prove sending" style="margin-top:5px">
<input type="text" name="txprvkey" size="120" placeholder="Tx private key"><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">
{{/enable_js}}
</form>
</div>
</div>
@ -122,6 +152,234 @@
</div>
{{/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}}

View File

@ -8,10 +8,14 @@
<div class="center">
<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/>
(In Linux, can get base64 signed raw tx data: <i>base64 signed_monero_tx | xclip -selection clipboard</i>)<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/>
<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>
<br/>
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

View File

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