Support for supercop ASM in wallet, and benchmark for supercop
This commit is contained in:
parent
5d850dde99
commit
a11ec4ac1d
|
@ -15,3 +15,7 @@
|
||||||
[submodule "external/randomx"]
|
[submodule "external/randomx"]
|
||||||
path = external/randomx
|
path = external/randomx
|
||||||
url = https://github.com/tevador/RandomX
|
url = https://github.com/tevador/RandomX
|
||||||
|
[submodule "external/supercop"]
|
||||||
|
path = external/supercop
|
||||||
|
url = https://github.com/monero-project/supercop
|
||||||
|
branch = monero
|
||||||
|
|
|
@ -210,6 +210,7 @@ if(NOT MANUAL_SUBMODULES)
|
||||||
check_submodule(external/rapidjson)
|
check_submodule(external/rapidjson)
|
||||||
check_submodule(external/trezor-common)
|
check_submodule(external/trezor-common)
|
||||||
check_submodule(external/randomx)
|
check_submodule(external/randomx)
|
||||||
|
check_submodule(external/supercop)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -311,7 +312,7 @@ endif()
|
||||||
# elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*")
|
# elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*")
|
||||||
# set(BSDI TRUE)
|
# set(BSDI TRUE)
|
||||||
|
|
||||||
include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external)
|
include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include)
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
include_directories(SYSTEM /usr/include/malloc)
|
include_directories(SYSTEM /usr/include/malloc)
|
||||||
|
@ -456,6 +457,9 @@ add_definition_if_function_found(strptime HAVE_STRPTIME)
|
||||||
|
|
||||||
add_definitions(-DAUTO_INITIALIZE_EASYLOGGINGPP)
|
add_definitions(-DAUTO_INITIALIZE_EASYLOGGINGPP)
|
||||||
|
|
||||||
|
set(MONERO_GENERATED_HEADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated_include")
|
||||||
|
include_directories(${MONERO_GENERATED_HEADERS_DIR})
|
||||||
|
|
||||||
# Generate header for embedded translations
|
# Generate header for embedded translations
|
||||||
# Generate header for embedded translations, use target toolchain if depends, otherwise use the
|
# Generate header for embedded translations, use target toolchain if depends, otherwise use the
|
||||||
# lrelease and lupdate binaries from the host
|
# lrelease and lupdate binaries from the host
|
||||||
|
@ -987,6 +991,7 @@ if(SODIUM_LIBRARY)
|
||||||
set(ZMQ_LIB "${ZMQ_LIB};${SODIUM_LIBRARY}")
|
set(ZMQ_LIB "${ZMQ_LIB};${SODIUM_LIBRARY}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
include(external/supercop/functions.cmake) # place after setting flags and before src directory inclusion
|
||||||
add_subdirectory(contrib)
|
add_subdirectory(contrib)
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# Set the system name to one of Android, Darwin, FreeBSD, Linux, or Windows
|
# Set the system name to one of Android, Darwin, FreeBSD, Linux, or Windows
|
||||||
SET(CMAKE_SYSTEM_NAME @depends@)
|
SET(CMAKE_SYSTEM_NAME @depends@)
|
||||||
|
SET(CMAKE_SYSTEM_PROCESSOR @arch@)
|
||||||
SET(CMAKE_BUILD_TYPE @release_type@)
|
SET(CMAKE_BUILD_TYPE @release_type@)
|
||||||
|
|
||||||
OPTION(STATIC "Link libraries statically" ON)
|
OPTION(STATIC "Link libraries statically" ON)
|
||||||
|
@ -63,14 +64,14 @@ set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # Find programs on host
|
||||||
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # Find libs in target
|
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # Find libs in target
|
||||||
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # Find includes in target
|
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # Find includes in target
|
||||||
|
|
||||||
set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_HOST_SYSTEM_PROCESSOR} CACHE STRING "" FORCE)
|
|
||||||
|
|
||||||
# specify the cross compiler to be used. Darwin uses clang provided by the SDK.
|
# specify the cross compiler to be used. Darwin uses clang provided by the SDK.
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||||
SET(CMAKE_C_COMPILER @prefix@/native/bin/clang)
|
SET(CMAKE_C_COMPILER @prefix@/native/bin/clang)
|
||||||
SET(CMAKE_C_COMPILER_TARGET x86_64-apple-darwin11)
|
SET(CMAKE_C_COMPILER_TARGET x86_64-apple-darwin11)
|
||||||
SET(CMAKE_CXX_COMPILER @prefix@/native/bin/clang++ -stdlib=libc++)
|
SET(CMAKE_CXX_COMPILER @prefix@/native/bin/clang++ -stdlib=libc++)
|
||||||
SET(CMAKE_CXX_COMPILER_TARGET x86_64-apple-darwin11)
|
SET(CMAKE_CXX_COMPILER_TARGET x86_64-apple-darwin11)
|
||||||
|
SET(CMAKE_ASM_COMPILER_TARGET x86_64-apple-darwin11)
|
||||||
|
SET(CMAKE_ASM-ATT_COMPILER_TARGET x86_64-apple-darwin11)
|
||||||
SET(_CMAKE_TOOLCHAIN_PREFIX x86_64-apple-darwin11-)
|
SET(_CMAKE_TOOLCHAIN_PREFIX x86_64-apple-darwin11-)
|
||||||
SET(APPLE True)
|
SET(APPLE True)
|
||||||
SET(BUILD_TAG "mac-x64")
|
SET(BUILD_TAG "mac-x64")
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 7d8b6878260061da56ade6d23dc833288659d0a3
|
|
@ -116,3 +116,6 @@ endif()
|
||||||
|
|
||||||
# cheat because cmake and ccache hate each other
|
# cheat because cmake and ccache hate each other
|
||||||
set_property(SOURCE CryptonightR_template.S PROPERTY LANGUAGE C)
|
set_property(SOURCE CryptonightR_template.S PROPERTY LANGUAGE C)
|
||||||
|
|
||||||
|
# Must be done last, because it references libraries in this directory
|
||||||
|
add_subdirectory(wallet)
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
# Copyright (c) 2020, The Monero Project
|
||||||
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
# permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
# conditions and the following disclaimer.
|
||||||
|
#
|
||||||
|
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
# of conditions and the following disclaimer in the documentation and/or other
|
||||||
|
# materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||||
|
# used to endorse or promote products derived from this software without specific
|
||||||
|
# prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#
|
||||||
|
# Possibly user defined values.
|
||||||
|
#
|
||||||
|
set(MONERO_WALLET_CRYPTO_LIBRARY "auto" CACHE STRING "Select a wallet crypto library")
|
||||||
|
|
||||||
|
#
|
||||||
|
# If the user specified "auto", detect best library defaulting to internal.
|
||||||
|
#
|
||||||
|
if (${MONERO_WALLET_CRYPTO_LIBRARY} STREQUAL "auto")
|
||||||
|
monero_crypto_autodetect(AVAILABLE BEST)
|
||||||
|
if (DEFINED BEST)
|
||||||
|
message("Wallet crypto is using ${BEST} backend")
|
||||||
|
set(MONERO_WALLET_CRYPTO_LIBRARY ${BEST})
|
||||||
|
else ()
|
||||||
|
message("Defaulting to internal crypto library for wallet")
|
||||||
|
set(MONERO_WALLET_CRYPTO_LIBRARY "cn")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Configure library target "wallet-crypto" - clients will use this as a
|
||||||
|
# library dependency which in turn will depend on the crypto library selected.
|
||||||
|
#
|
||||||
|
if (${MONERO_WALLET_CRYPTO_LIBRARY} STREQUAL "cn")
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/empty.h.in ${MONERO_GENERATED_HEADERS_DIR}/crypto/wallet/ops.h)
|
||||||
|
add_library(wallet-crypto ALIAS cncrypto)
|
||||||
|
else ()
|
||||||
|
monero_crypto_generate_header(${MONERO_WALLET_CRYPTO_LIBRARY} "${MONERO_GENERATED_HEADERS_DIR}/crypto/wallet/ops.h")
|
||||||
|
monero_crypto_get_target(${MONERO_WALLET_CRYPTO_LIBRARY} CRYPTO_TARGET)
|
||||||
|
add_library(wallet-crypto $<TARGET_OBJECTS:${CRYPTO_TARGET}>)
|
||||||
|
target_link_libraries(wallet-crypto cncrypto)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright (c) 2020, The Monero Project
|
||||||
|
//
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
// permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
// of conditions and the following disclaimer in the documentation and/or other
|
||||||
|
// materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without specific
|
||||||
|
// prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include "crypto/wallet/ops.h"
|
||||||
|
|
||||||
|
namespace crypto {
|
||||||
|
namespace wallet {
|
||||||
|
// if C functions defined from external/supercop - cmake generates crypto/wallet/ops.h
|
||||||
|
#if defined(monero_crypto_generate_key_derivation)
|
||||||
|
inline
|
||||||
|
bool generate_key_derivation(const public_key &tx_pub, const secret_key &view_sec, key_derivation &out)
|
||||||
|
{
|
||||||
|
return monero_crypto_generate_key_derivation(out.data, tx_pub.data, view_sec.data) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool derive_subaddress_public_key(const public_key &output_pub, const key_derivation &d, std::size_t index, public_key &out)
|
||||||
|
{
|
||||||
|
ec_scalar scalar;
|
||||||
|
derivation_to_scalar(d, index, scalar);
|
||||||
|
return monero_crypto_generate_subaddress_public_key(out.data, output_pub.data, scalar.data) == 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
using ::crypto::generate_key_derivation;
|
||||||
|
using ::crypto::derive_subaddress_public_key;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) 2020, The Monero Project
|
||||||
|
//
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
// permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
// of conditions and the following disclaimer in the documentation and/or other
|
||||||
|
// materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without specific
|
||||||
|
// prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Left empty so internal cryptonote crypto library is used.
|
|
@ -72,6 +72,7 @@ target_link_libraries(device
|
||||||
${HIDAPI_LIBRARIES}
|
${HIDAPI_LIBRARIES}
|
||||||
cncrypto
|
cncrypto
|
||||||
ringct_basic
|
ringct_basic
|
||||||
|
wallet-crypto
|
||||||
${OPENSSL_CRYPTO_LIBRARIES}
|
${OPENSSL_CRYPTO_LIBRARIES}
|
||||||
${Boost_SERIALIZATION_LIBRARY}
|
${Boost_SERIALIZATION_LIBRARY}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
#include "device_default.hpp"
|
#include "device_default.hpp"
|
||||||
#include "int-util.h"
|
#include "int-util.h"
|
||||||
|
#include "crypto/wallet/crypto.h"
|
||||||
#include "cryptonote_basic/account.h"
|
#include "cryptonote_basic/account.h"
|
||||||
#include "cryptonote_basic/subaddress_index.h"
|
#include "cryptonote_basic/subaddress_index.h"
|
||||||
#include "cryptonote_core/cryptonote_tx_utils.h"
|
#include "cryptonote_core/cryptonote_tx_utils.h"
|
||||||
|
@ -120,7 +121,7 @@ namespace hw {
|
||||||
/* ======================================================================= */
|
/* ======================================================================= */
|
||||||
|
|
||||||
bool device_default::derive_subaddress_public_key(const crypto::public_key &out_key, const crypto::key_derivation &derivation, const std::size_t output_index, crypto::public_key &derived_key) {
|
bool device_default::derive_subaddress_public_key(const crypto::public_key &out_key, const crypto::key_derivation &derivation, const std::size_t output_index, crypto::public_key &derived_key) {
|
||||||
return crypto::derive_subaddress_public_key(out_key, derivation, output_index,derived_key);
|
return crypto::wallet::derive_subaddress_public_key(out_key, derivation, output_index,derived_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
crypto::public_key device_default::get_subaddress_spend_public_key(const cryptonote::account_keys& keys, const cryptonote::subaddress_index &index) {
|
crypto::public_key device_default::get_subaddress_spend_public_key(const cryptonote::account_keys& keys, const cryptonote::subaddress_index &index) {
|
||||||
|
@ -236,7 +237,7 @@ namespace hw {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool device_default::generate_key_derivation(const crypto::public_key &key1, const crypto::secret_key &key2, crypto::key_derivation &derivation) {
|
bool device_default::generate_key_derivation(const crypto::public_key &key1, const crypto::secret_key &key2, crypto::key_derivation &derivation) {
|
||||||
return crypto::generate_key_derivation(key1, key2, derivation);
|
return crypto::wallet::generate_key_derivation(key1, key2, derivation);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool device_default::derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res){
|
bool device_default::derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res){
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
#
|
#
|
||||||
# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||||
|
|
||||||
|
set(MONERO_WALLET_CRYPTO_BENCH "auto" CACHE STRING "Select wallet crypto libraries for benchmarking")
|
||||||
|
|
||||||
# The docs say this only affects grouping in IDEs
|
# The docs say this only affects grouping in IDEs
|
||||||
set(folder "tests")
|
set(folder "tests")
|
||||||
set(TEST_DATA_DIR "${CMAKE_CURRENT_LIST_DIR}/data")
|
set(TEST_DATA_DIR "${CMAKE_CURRENT_LIST_DIR}/data")
|
||||||
|
@ -118,6 +120,41 @@ add_test(
|
||||||
NAME hash-target
|
NAME hash-target
|
||||||
COMMAND hash-target-tests)
|
COMMAND hash-target-tests)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Configure wallet crypto benchmark
|
||||||
|
#
|
||||||
|
if (${MONERO_WALLET_CRYPTO_BENCH} STREQUAL "auto")
|
||||||
|
set(MONERO_WALLET_CRYPTO_BENCH "cn")
|
||||||
|
monero_crypto_autodetect(AVAILABLE BEST)
|
||||||
|
if (DEFINED AVAILABLE)
|
||||||
|
list(APPEND MONERO_WALLET_CRYPTO_BENCH ${AVAILABLE})
|
||||||
|
endif ()
|
||||||
|
message("Wallet crypto bench is using ${MONERO_WALLET_CRYPTO_BENCH}")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
list(REMOVE_DUPLICATES MONERO_WALLET_CRYPTO_BENCH)
|
||||||
|
list(REMOVE_ITEM MONERO_WALLET_CRYPTO_BENCH "cn") # always used for comparison
|
||||||
|
set(MONERO_WALLET_CRYPTO_BENCH_NAMES "(cn)")
|
||||||
|
foreach(BENCH IN LISTS MONERO_WALLET_CRYPTO_BENCH)
|
||||||
|
monero_crypto_valid(${BENCH} VALID)
|
||||||
|
if (NOT VALID)
|
||||||
|
message(FATAL_ERROR "Invalid MONERO_WALLET_CRYPTO_BENCH option ${BENCH}")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
monero_crypto_get_target(${BENCH} BENCH_LIBRARY)
|
||||||
|
list(APPEND BENCH_OBJECTS $<TARGET_OBJECTS:${BENCH_LIBRARY}>)
|
||||||
|
|
||||||
|
monero_crypto_get_namespace(${BENCH} BENCH_NAMESPACE)
|
||||||
|
set(MONERO_WALLET_CRYPTO_BENCH_NAMES "${MONERO_WALLET_CRYPTO_BENCH_NAMES}(${BENCH_NAMESPACE})")
|
||||||
|
endforeach ()
|
||||||
|
|
||||||
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/benchmark.h.in" "${MONERO_GENERATED_HEADERS_DIR}/tests/benchmark.h")
|
||||||
|
add_executable(monero-wallet-crypto-bench benchmark.cpp ${BENCH_OBJECTS})
|
||||||
|
target_link_libraries(monero-wallet-crypto-bench cncrypto)
|
||||||
|
|
||||||
|
add_test(NAME wallet-crypto-bench COMMAND monero-wallet-crypto-bench)
|
||||||
|
|
||||||
|
|
||||||
set(enabled_tests
|
set(enabled_tests
|
||||||
core_tests
|
core_tests
|
||||||
difficulty
|
difficulty
|
||||||
|
|
|
@ -0,0 +1,437 @@
|
||||||
|
// Copyright (c) 2020, The Monero Project
|
||||||
|
//
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
// permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
// of conditions and the following disclaimer in the documentation and/or other
|
||||||
|
// materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without specific
|
||||||
|
// prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include "tests/benchmark.h"
|
||||||
|
|
||||||
|
#include <boost/fusion/adapted/std_tuple.hpp>
|
||||||
|
#include <boost/fusion/algorithm/iteration/fold.hpp>
|
||||||
|
#include <boost/preprocessor/seq/enum.hpp>
|
||||||
|
#include <boost/preprocessor/seq/for_each.hpp>
|
||||||
|
#include <boost/preprocessor/seq/seq.hpp>
|
||||||
|
#include <boost/preprocessor/seq.hpp>
|
||||||
|
#include <boost/preprocessor/stringize.hpp>
|
||||||
|
#include <boost/spirit/include/karma_char.hpp>
|
||||||
|
#include <boost/spirit/include/karma_format.hpp>
|
||||||
|
#include <boost/spirit/include/karma_repeat.hpp>
|
||||||
|
#include <boost/spirit/include/karma_right_alignment.hpp>
|
||||||
|
#include <boost/spirit/include/karma_sequence.hpp>
|
||||||
|
#include <boost/spirit/include/karma_string.hpp>
|
||||||
|
#include <boost/spirit/include/karma_uint.hpp>
|
||||||
|
#include <boost/spirit/include/qi_char.hpp>
|
||||||
|
#include <boost/spirit/include/qi_list.hpp>
|
||||||
|
#include <boost/spirit/include/qi_parse.hpp>
|
||||||
|
#include <boost/spirit/include/qi_uint.hpp>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstring>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "crypto/crypto.h"
|
||||||
|
#include "cryptonote_basic/cryptonote_basic.h"
|
||||||
|
#include "monero/crypto/amd64-64-24k.h"
|
||||||
|
#include "monero/crypto/amd64-51-30k.h"
|
||||||
|
|
||||||
|
#define CHECK(...) \
|
||||||
|
if(!( __VA_ARGS__ )) \
|
||||||
|
throw std::runtime_error{ \
|
||||||
|
"TEST FAILED (line " \
|
||||||
|
BOOST_PP_STRINGIZE( __LINE__ ) \
|
||||||
|
"): " \
|
||||||
|
BOOST_PP_STRINGIZE( __VA_ARGS__ ) \
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Define function that forwards arguments to `crypto::func`.
|
||||||
|
#define FORWARD_FUNCTION(func) \
|
||||||
|
template<typename... T> \
|
||||||
|
static bool func (T&&... args) \
|
||||||
|
{ \
|
||||||
|
return ::crypto:: func (std::forward<T>(args)...); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CRYPTO_FUNCTION(library, func) \
|
||||||
|
BOOST_PP_CAT(BOOST_PP_CAT(monero_crypto_, library), func)
|
||||||
|
|
||||||
|
#define CRYPTO_BENCHMARK(r, _, library) \
|
||||||
|
struct library \
|
||||||
|
{ \
|
||||||
|
static constexpr const char* name() noexcept { return BOOST_PP_STRINGIZE(library); } \
|
||||||
|
static bool generate_key_derivation(const ::crypto::public_key &tx_pub, const ::crypto::secret_key &view_sec, ::crypto::key_derivation &out) \
|
||||||
|
{ \
|
||||||
|
return CRYPTO_FUNCTION(library, _generate_key_derivation) (out.data, tx_pub.data, view_sec.data) == 0; \
|
||||||
|
} \
|
||||||
|
static bool derive_subaddress_public_key(const ::crypto::public_key &spend_pub, const ::crypto::key_derivation &d, std::size_t index, ::crypto::public_key &out) \
|
||||||
|
{ \
|
||||||
|
::crypto::ec_scalar scalar; \
|
||||||
|
::crypto::derivation_to_scalar(d, index, scalar); \
|
||||||
|
return CRYPTO_FUNCTION(library, _generate_subaddress_public_key) (out.data, spend_pub.data, scalar.data) == 0; \
|
||||||
|
} \
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
//! Default number of iterations for benchmark timing.
|
||||||
|
constexpr const unsigned default_iterations = 1000;
|
||||||
|
|
||||||
|
//! \return Byte compare two objects of `T`.
|
||||||
|
template<typename T>
|
||||||
|
bool compare(const T& lhs, const T& rhs) noexcept
|
||||||
|
{
|
||||||
|
static_assert(!epee::has_padding<T>(), "type might have padding");
|
||||||
|
return std::memcmp(std::addressof(lhs), std::addressof(rhs), sizeof(T)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Benchmark default monero crypto library - a re-arranged ref10 implementation.
|
||||||
|
struct cn
|
||||||
|
{
|
||||||
|
static constexpr const char* name() noexcept { return "cn"; }
|
||||||
|
FORWARD_FUNCTION( generate_key_derivation );
|
||||||
|
FORWARD_FUNCTION( derive_subaddress_public_key );
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define functions for every library except for `cn` which is the head library.
|
||||||
|
BOOST_PP_SEQ_FOR_EACH(CRYPTO_BENCHMARK, _, BOOST_PP_SEQ_TAIL(BENCHMARK_LIBRARIES));
|
||||||
|
|
||||||
|
// All enabled benchmark libraries
|
||||||
|
using enabled_libraries = std::tuple<BOOST_PP_SEQ_ENUM(BENCHMARK_LIBRARIES)>;
|
||||||
|
|
||||||
|
|
||||||
|
//! Callable that runs a benchmark against all enabled libraries
|
||||||
|
template<typename R>
|
||||||
|
struct run_benchmark
|
||||||
|
{
|
||||||
|
using result = R;
|
||||||
|
|
||||||
|
template<typename B>
|
||||||
|
result operator()(result out, const B benchmark) const
|
||||||
|
{
|
||||||
|
using inner_result = typename B::result;
|
||||||
|
out.push_back({boost::fusion::fold(enabled_libraries{}, inner_result{}, benchmark), benchmark.name()});
|
||||||
|
std::sort(out.back().first.begin(), out.back().first.end());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Run 0+ benchmarks against all enabled libraries
|
||||||
|
template<typename R, typename... B>
|
||||||
|
R run_benchmarks(B&&... benchmarks)
|
||||||
|
{
|
||||||
|
auto out = boost::fusion::fold(std::make_tuple(std::forward<B>(benchmarks)...), R{}, run_benchmark<R>{});
|
||||||
|
std::sort(out.begin(), out.end());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Run a suite of benchmarks - allows for comparison against a subset of benchmarks
|
||||||
|
template<typename S>
|
||||||
|
std::pair<typename S::result, std::string> run_suite(const S& suite)
|
||||||
|
{
|
||||||
|
return {suite(), suite.name()};
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Arguments given to every crypto library being benchmarked.
|
||||||
|
struct bench_args
|
||||||
|
{
|
||||||
|
explicit bench_args(unsigned iterations)
|
||||||
|
: iterations(iterations), one(), two()
|
||||||
|
{
|
||||||
|
crypto::generate_keys(one.pub, one.sec, one.sec, false);
|
||||||
|
crypto::generate_keys(two.pub, two.sec, two.sec, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned iterations;
|
||||||
|
cryptonote::keypair one;
|
||||||
|
cryptonote::keypair two;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! Tests the ECDH step used for monero txes where the tx-pub is always
|
||||||
|
de-compressed into a table every time. */
|
||||||
|
struct tx_pub_standard
|
||||||
|
{
|
||||||
|
using result = std::vector<std::pair<std::chrono::steady_clock::duration, std::string>>;
|
||||||
|
static constexpr const char* name() noexcept { return "standard"; }
|
||||||
|
|
||||||
|
const bench_args args;
|
||||||
|
|
||||||
|
template<typename L>
|
||||||
|
result operator()(result out, const L library) const
|
||||||
|
{
|
||||||
|
crypto::key_derivation us;
|
||||||
|
crypto::key_derivation them;
|
||||||
|
CHECK(crypto::generate_key_derivation(args.one.pub, args.two.sec, them));
|
||||||
|
CHECK(library.generate_key_derivation(args.one.pub, args.two.sec, us));
|
||||||
|
CHECK(compare(us, them));
|
||||||
|
|
||||||
|
unsigned i = 0;
|
||||||
|
for (unsigned j = 0; j < 100; ++j)
|
||||||
|
i += library.generate_key_derivation(args.one.pub, args.two.sec, us);
|
||||||
|
CHECK(i == 100);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
const auto start = std::chrono::steady_clock::now();
|
||||||
|
for (unsigned j = 0; j < args.iterations; ++j)
|
||||||
|
i += library.generate_key_derivation(args.one.pub, args.two.sec, us);
|
||||||
|
const auto end = std::chrono::steady_clock::now();
|
||||||
|
CHECK(i == args.iterations);
|
||||||
|
CHECK(compare(us, them));
|
||||||
|
|
||||||
|
out.push_back({end - start, library.name()});
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Tests various possible optimizations for tx ECDH-step.
|
||||||
|
struct tx_pub_suite
|
||||||
|
{
|
||||||
|
using result = std::vector<std::pair<tx_pub_standard::result, std::string>>;
|
||||||
|
static constexpr const char* name() noexcept { return "generate_key_derivation step"; }
|
||||||
|
|
||||||
|
const bench_args args;
|
||||||
|
|
||||||
|
result operator()() const
|
||||||
|
{
|
||||||
|
return run_benchmarks<result>(tx_pub_standard{args});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! Tests the shared-secret to output-key step used for monero txes where
|
||||||
|
the users spend-public is always de-compressed. */
|
||||||
|
struct output_pub_standard
|
||||||
|
{
|
||||||
|
using result = std::vector<std::pair<std::chrono::steady_clock::duration, std::string>>;
|
||||||
|
static constexpr const char* name() noexcept { return "standard"; }
|
||||||
|
|
||||||
|
const bench_args args;
|
||||||
|
|
||||||
|
template<typename L>
|
||||||
|
result operator()(result out, const L library) const
|
||||||
|
{
|
||||||
|
crypto::key_derivation derived;
|
||||||
|
crypto::public_key us;
|
||||||
|
crypto::public_key them;
|
||||||
|
CHECK(crypto::generate_key_derivation(args.one.pub, args.two.sec, derived));
|
||||||
|
CHECK(library.derive_subaddress_public_key(args.two.pub, derived, 0, us));
|
||||||
|
CHECK(crypto::derive_subaddress_public_key(args.two.pub, derived, 0, them));
|
||||||
|
CHECK(compare(us, them));
|
||||||
|
|
||||||
|
unsigned i = 0;
|
||||||
|
for (unsigned j = 0; j < 100; ++j)
|
||||||
|
i += library.derive_subaddress_public_key(args.two.pub, derived, j, us);
|
||||||
|
CHECK(i == 100);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
const auto start = std::chrono::steady_clock::now();
|
||||||
|
for (unsigned j = 0; j < args.iterations; ++j)
|
||||||
|
i += library.derive_subaddress_public_key(args.two.pub, derived, j, us);
|
||||||
|
const auto end = std::chrono::steady_clock::now();
|
||||||
|
CHECK(i == args.iterations);
|
||||||
|
|
||||||
|
out.push_back({end - start, library.name()});
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Tests various possible optimizations for shared-secret to output-key step.
|
||||||
|
struct output_pub_suite
|
||||||
|
{
|
||||||
|
using result = std::vector<std::pair<output_pub_standard::result, std::string>>;
|
||||||
|
static constexpr const char* name() noexcept { return "derive_subaddress_public_key step"; }
|
||||||
|
|
||||||
|
const bench_args args;
|
||||||
|
|
||||||
|
result operator()() const
|
||||||
|
{
|
||||||
|
return run_benchmarks<result>(output_pub_standard{args});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tx_bench_args
|
||||||
|
{
|
||||||
|
const bench_args main;
|
||||||
|
unsigned outputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! Simulates "standard" tx scanning where a tx-pubkey is de-compressed into
|
||||||
|
a table and user spend-public is de-compressed, every time. */
|
||||||
|
struct tx_standard
|
||||||
|
{
|
||||||
|
using result = std::vector<std::pair<std::chrono::steady_clock::duration, std::string>>;
|
||||||
|
static constexpr const char* name() noexcept { return "standard"; }
|
||||||
|
|
||||||
|
const tx_bench_args args;
|
||||||
|
|
||||||
|
template<typename L>
|
||||||
|
result operator()(result out, const L library) const
|
||||||
|
{
|
||||||
|
crypto::key_derivation derived_us;
|
||||||
|
crypto::key_derivation derived_them;
|
||||||
|
crypto::public_key us;
|
||||||
|
crypto::public_key them;
|
||||||
|
CHECK(library.generate_key_derivation(args.main.one.pub, args.main.two.sec, derived_us));
|
||||||
|
CHECK(crypto::generate_key_derivation(args.main.one.pub, args.main.two.sec, derived_them));
|
||||||
|
CHECK(library.derive_subaddress_public_key(args.main.two.pub, derived_us, 0, us));
|
||||||
|
CHECK(crypto::derive_subaddress_public_key(args.main.two.pub, derived_them, 0, them));
|
||||||
|
CHECK(compare(us, them));
|
||||||
|
|
||||||
|
unsigned i = 0;
|
||||||
|
for (unsigned j = 0; j < 100; ++j)
|
||||||
|
{
|
||||||
|
i += library.generate_key_derivation(args.main.one.pub, args.main.two.sec, derived_us);
|
||||||
|
i += library.derive_subaddress_public_key(args.main.two.pub, derived_us, j, us);
|
||||||
|
}
|
||||||
|
CHECK(i == 200);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
const auto start = std::chrono::steady_clock::now();
|
||||||
|
for (unsigned j = 0; j < args.main.iterations; ++j)
|
||||||
|
{
|
||||||
|
i += library.generate_key_derivation(args.main.one.pub, args.main.two.sec, derived_us);
|
||||||
|
for (unsigned k = 0; k < args.outputs; ++k)
|
||||||
|
i += library.derive_subaddress_public_key(args.main.two.pub, derived_us, k, us);
|
||||||
|
}
|
||||||
|
const auto end = std::chrono::steady_clock::now();
|
||||||
|
CHECK(i == args.main.iterations + args.main.iterations * args.outputs);
|
||||||
|
|
||||||
|
out.push_back({end - start, library.name()});
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Tests various possible optimizations for tx scanning.
|
||||||
|
struct tx_suite
|
||||||
|
{
|
||||||
|
using result = std::vector<std::pair<output_pub_standard::result, std::string>>;
|
||||||
|
std::string name() const { return "Transactions with " + std::to_string(args.outputs) + " outputs"; }
|
||||||
|
|
||||||
|
const tx_bench_args args;
|
||||||
|
|
||||||
|
result operator()() const
|
||||||
|
{
|
||||||
|
return run_benchmarks<result>(tx_standard{args});
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::chrono::steady_clock::duration print(const tx_pub_standard::result& leaf, std::ostream& out, unsigned depth)
|
||||||
|
{
|
||||||
|
namespace karma = boost::spirit::karma;
|
||||||
|
const std::size_t align = leaf.empty() ?
|
||||||
|
0 : std::to_string(leaf.back().first.count()).size();
|
||||||
|
const auto best = leaf.empty() ?
|
||||||
|
std::chrono::steady_clock::duration::max() : leaf.front().first;
|
||||||
|
for (auto const& entry : leaf)
|
||||||
|
{
|
||||||
|
out << karma::format(karma::repeat(depth ? depth - 1 : 0)["| "]) << '|';
|
||||||
|
out << karma::format((karma::right_align(std::min(20u - depth, 20u), '-')["> " << karma::string]), entry.second);
|
||||||
|
out << " => " << karma::format((karma::right_align(align)[karma::uint_]), entry.first.count());
|
||||||
|
out << " ns (+";
|
||||||
|
out << (double((entry.first - best).count()) / best.count()) * 100 << "%)" << std::endl;
|
||||||
|
}
|
||||||
|
out << karma::format(karma::repeat(depth ? depth - 1 : 0)["| "]) << std::endl;
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::chrono::steady_clock::duration
|
||||||
|
print(const std::vector<std::pair<T, std::string>>& node, std::ostream& out, unsigned depth)
|
||||||
|
{
|
||||||
|
auto best = std::chrono::steady_clock::duration::max();
|
||||||
|
for (auto const& entry : node)
|
||||||
|
{
|
||||||
|
std::stringstream buffer{};
|
||||||
|
auto last = print(entry.first, buffer, depth + 1);
|
||||||
|
if (last != std::chrono::steady_clock::duration::max())
|
||||||
|
{
|
||||||
|
namespace karma = boost::spirit::karma;
|
||||||
|
best = std::min(best, last);
|
||||||
|
out << karma::format(karma::repeat(depth)["|-"]);
|
||||||
|
out << "+ " << entry.second << ' ';
|
||||||
|
out << last.count() << " ns (+";
|
||||||
|
out << (double((last - best).count()) / best.count()) * 100 << "%)" << std::endl;
|
||||||
|
out << buffer.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
using results = std::vector<std::pair<tx_pub_suite::result, std::string>>;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
unsigned iterations = default_iterations;
|
||||||
|
std::vector<unsigned> nums{};
|
||||||
|
if (2 <= argc) iterations = std::stoul(argv[1]);
|
||||||
|
if (3 <= argc)
|
||||||
|
{
|
||||||
|
namespace qi = boost::spirit::qi;
|
||||||
|
if (!qi::parse(argv[2], argv[2] + strlen(argv[2]), (qi::uint_ % ','), nums))
|
||||||
|
throw std::runtime_error{"bad tx outputs string"};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nums = {2, 4};
|
||||||
|
}
|
||||||
|
std::sort(nums.begin(), nums.end());
|
||||||
|
nums.erase(std::unique(nums.begin(), nums.end()), nums.end());
|
||||||
|
|
||||||
|
std::cout << "Running benchmark using " << iterations << " iterations" << std::endl;
|
||||||
|
|
||||||
|
const bench_args args{iterations};
|
||||||
|
|
||||||
|
results val{};
|
||||||
|
|
||||||
|
std::cout << "Transaction Component Benchmarks" << std::endl;
|
||||||
|
std::cout << "--------------------------------" << std::endl;
|
||||||
|
val.push_back(run_suite(tx_pub_suite{args}));
|
||||||
|
val.push_back(run_suite(output_pub_suite{args}));
|
||||||
|
std::sort(val.begin(), val.end());
|
||||||
|
print(val, std::cout, 0);
|
||||||
|
|
||||||
|
val.clear();
|
||||||
|
std::cout << "Transaction Benchmarks" << std::endl;
|
||||||
|
std::cout << "----------------------" << std::endl;
|
||||||
|
for (const unsigned num : nums)
|
||||||
|
val.push_back(run_suite(tx_suite{{args, num}}));
|
||||||
|
std::sort(val.begin(), val.end());
|
||||||
|
print(val, std::cout, 0);
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: " << e.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// A Boost PP sequence
|
||||||
|
#define BENCHMARK_LIBRARIES @MONERO_WALLET_CRYPTO_BENCH_NAMES@
|
||||||
|
|
Loading…
Reference in New Issue